{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.13.1\n",
      "1.16.4\n"
     ]
    }
   ],
   "source": [
    "# Import libraries and modules\n",
    "import sys\n",
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "import seaborn as sns\n",
    "import shutil\n",
    "print(tf.__version__)\n",
    "print(np.__version__)\n",
    "np.set_printoptions(threshold=np.inf)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# tf.enable_eager_execution()\n",
    "tf.executing_eagerly()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Create data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create data generator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "simple_data_gen = False\n",
    "\n",
    "pct_seq_before_anom = 70.0\n",
    "pct_seq_after_anom = 0.0\n",
    "\n",
    "\n",
    "def create_time_series_norm_params(simple_data_gen):\n",
    "  \"\"\"Creates \"normal\" time series sine parameters.\n",
    "\n",
    "  Returns multiple \"normal\" sequences.\n",
    "\n",
    "  Args:\n",
    "    simple_data_gen: Bool that determines if we are making simple data\n",
    "      or more complex.\n",
    "\n",
    "  Returns:\n",
    "    np.array of shape = (num_seq, seq_len) of \"normal\" sequences.\n",
    "  \"\"\"\n",
    "  norm_freq_scale = 1.0\n",
    "  norm_freq_shift = 1.0\n",
    "\n",
    "  norm_ampl_scale = 1.0\n",
    "  norm_ampl_shift = 1.0\n",
    "\n",
    "  norm_noise = 1.0\n",
    "\n",
    "  if simple_data_gen:\n",
    "    norm_freq = 1.0\n",
    "    norm_ampl = 1.0\n",
    "  else:\n",
    "    norm_freq = np.random.random() * norm_freq_scale + norm_freq_shift\n",
    "    norm_ampl = np.random.random() * norm_ampl_scale + norm_ampl_shift\n",
    "\n",
    "  return {\"norm_freq\": norm_freq,\n",
    "          \"norm_ampl\": norm_ampl,\n",
    "          \"norm_noise\": norm_noise}\n",
    "\n",
    "\n",
    "def create_time_series_normal(\n",
    "    simple_data_gen,\n",
    "    num_seq,\n",
    "    seq_len,\n",
    "    norm_freq,\n",
    "    norm_ampl,\n",
    "    norm_noise):\n",
    "  \"\"\"Creates normal time series data.\n",
    "\n",
    "  Given the number of sequences to create, the sequence length, and \"normal\"\n",
    "  parameters for sine, returns multiple \"normal\" sequences.\n",
    "\n",
    "  Args:\n",
    "    simple_data_gen: Bool that determines if we are making simple data\n",
    "      or more complex.\n",
    "    num_seq: Number of sequences we want to create.\n",
    "    seq_len: Number of timesteps to create for each sequence.\n",
    "    norm_freq: Sine frequency for \"normal\" sequences.\n",
    "    norm_ampl: Sine amplitude for \"normal\" sequences.\n",
    "    norm_noise: Noise level for \"normal\" sequences.\n",
    "\n",
    "  Returns:\n",
    "    np.array of shape = (num_seq, seq_len) of \"normal\" sequences\n",
    "  \"\"\"\n",
    "  if simple_data_gen:\n",
    "    sequence = np.stack(\n",
    "        arrays=[np.sin(np.arange(seq_len) * norm_freq) * norm_ampl\n",
    "                for _ in range(num_seq)],\n",
    "        axis=0)\n",
    "  else:\n",
    "    sequence = np.stack(\n",
    "        arrays=[np.sin(np.arange(seq_len) * norm_freq) * norm_ampl + \\\n",
    "                  [np.random.random() * norm_noise\n",
    "                   for _ in range(seq_len)] for _ in range(num_seq)],\n",
    "        axis=0)\n",
    "\n",
    "  return sequence\n",
    "\n",
    "\n",
    "def create_time_series_with_anomaly(\n",
    "    simple_data_gen,\n",
    "    num_seq,\n",
    "    seq_len,\n",
    "    pct_seq_before_anom,\n",
    "    pct_seq_after_anom,\n",
    "    norm_freq,\n",
    "    norm_ampl,\n",
    "    norm_noise):\n",
    "  \"\"\"Creates anomalous time series data.\n",
    "\n",
    "  Given the number of sequences to create, the sequence length, and \"normal\"\n",
    "  parameters for sine, returns multiple \"normal\" sequences.\n",
    "\n",
    "  Args:\n",
    "    simple_data_gen: Bool that determines if we are making simple data\n",
    "      or more complex.\n",
    "    num_seq: Number of sequences we want to create.\n",
    "    seq_len: Number of timesteps to create for each sequence.\n",
    "    pct_seq_before_anom: Percent of sequence that will be \"normal\" before\n",
    "      anomaly section.\n",
    "    pct_seq_after_anom: Percent of sequence that will be \"normal\" after\n",
    "      anomaly section.\n",
    "    norm_freq: Sine frequency for \"normal\" sequences.\n",
    "    norm_ampl: Sine amplitude for \"normal\" sequences.\n",
    "    norm_noise: Noise level for \"normal\" sequences.\n",
    "\n",
    "  Returns:\n",
    "    np.array of shape = (num_seq, seq_len) of \"normal\" sequences\n",
    "  \"\"\"\n",
    "  seq_len_bf_anom = int(seq_len * pct_seq_before_anom / 100.0)\n",
    "  seq_len_af_anom = int(seq_len * pct_seq_after_anom / 100.0)\n",
    "  seq_len_anom = seq_len - seq_len_bf_anom - seq_len_af_anom\n",
    "\n",
    "  # Extra anomalous parameters\n",
    "  anom_ampl_multi_min = 8.0\n",
    "  anom_ampl_multi_max = 20.0\n",
    "\n",
    "  if simple_data_gen:\n",
    "    sequence_with_anomaly = np.stack(\n",
    "        arrays=[np.sin(np.arange(seq_len) * norm_freq) * norm_ampl\n",
    "                for _ in range(num_seq)],\n",
    "        axis=0)\n",
    "  else:\n",
    "    sequence_with_anomaly = create_time_series_normal(\n",
    "        simple_data_gen, num_seq, seq_len, norm_freq, norm_ampl, norm_noise)\n",
    "\n",
    "  sequence_with_anomaly[:, seq_len_bf_anom:seq_len_bf_anom + seq_len_anom] *= \\\n",
    "    ((anom_ampl_multi_max - anom_ampl_multi_min) * \\\n",
    "     np.random.random_sample([num_seq, seq_len_anom]) + anom_ampl_multi_min) * \\\n",
    "    (np.random.randint(2, size=[num_seq, seq_len_anom]) * -2 + 1)\n",
    "\n",
    "  return sequence_with_anomaly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_norm_params = create_time_series_norm_params(\n",
    "    simple_data_gen=simple_data_gen)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.5/dist-packages/seaborn/timeseries.py:183: UserWarning: The `tsplot` function is deprecated and will be removed in a future release. Please update your code to use the new `lineplot` function.\n",
      "  warnings.warn(msg, UserWarning)\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "flatui = [\"#9b59b6\", \"#3498db\", \"#95a5a6\", \"#e74c3c\", \"#34495e\", \"#2ecc71\"]\n",
    "for i in range(0, 5):\n",
    "  data = create_time_series_normal(\n",
    "      simple_data_gen=simple_data_gen,\n",
    "      num_seq=1,\n",
    "      seq_len=10,\n",
    "      norm_freq=test_norm_params[\"norm_freq\"],\n",
    "      norm_ampl=test_norm_params[\"norm_ampl\"],\n",
    "      norm_noise=test_norm_params[\"norm_noise\"])\n",
    "\n",
    "  sns.tsplot(data=data.reshape(-1), color=flatui[i%len(flatui)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAD8CAYAAAB0IB+mAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzs3XdcHOeB//HPzPYFlrLUBSSaEEWoooJkGdmWbMWOa4qdajvt7nLJJfkld2mX3C+55Frukvwul3pxEufs5BI7LnGTbdlWsxqoAqKIJmCXvrAs23dnfn8sQsJCEqKqPO/XSy+W2Zl5ngX03dlnniKpqoogCIJw/ZMXugKCIAjC/BCBLwiCcIMQgS8IgnCDEIEvCIJwgxCBLwiCcIMQgS8IgnCDEIEvCIJwgxCBLwiCcIMQgS8IgnCD0C50Bc6XnJys5uTkLHQ1BEEQrilHjhwZUFU15XL7XVWBn5OTQ3V19UJXQxAE4ZoiSdKZqewnmnQEQRBuECLwBUEQbhAi8AVBEG4QIvAFQRBuECLwBUEQbhAi8AVBEG4QIvAFQRBuECLwBUG46igRhYY3Ogh6QwtdleuKCHxBEK46jbu62PdYHY1vdS10Va4rIvAFQbiqBH1hjjx1GgDHqcEFrs31RQS+IAhXlZMvtOIfCZJSEE9PgxMloix0la4bIvAFQbhqeAZ91LzcRl5FBmV35hLyRRhoG1noal03ROALgnDVqH7qNKqisvahQjKKkwBw1IlmndkiAl8QhKvCYPsIp/faKd2eQ1yKGVO8gcTsOBH4s0gEviAIC05VVQ492YAhRsfKe/PHt9tKk+htHCISiixg7a4fIvAFQVhwXScGcNQNsuqBAgwxuvHttlIrkZBCX/PwAtbu+iECXxCEBaVEFA492YAlzUzx1kUTnksvSkKSwFHnXKDaXV9E4AuCsKCadtsZto+y9gNL0WgnRpIhRkdybrxox58lIvAFQVgwIX+YI081kVaYSM7atEn3ySi10tc8TMgfnufaXX9E4AuCsGBOvtiGzxVk/YeLkCRp0n1spUmoEZXexqF5rt31RwS+IAgLwuP0c/LFVvI2ZJBakDDhub4BJ5/+2r/y5r4q0goTkTWSmGZhFmgXugKCINyYjjx9bpDV+VRV5Z//63Gqjp+i6vgp/uZjD5JSkChu3M4CEfiCIMy7wY4RmnZ3UXZnLnGp5gnP7dh1gH2Hj/Pph99LU2sH//mrP7B56WqKewsIjIYwxOouclbhckTgC4Iw7w4/2YjBPHGQFcCAc5j/+NkTlBXl89H33oUkQWZ6Co8/9RJtWgc3HV9G8U2LF6jW175ZacOXJOlXkiT1SZJUe962JEmSXpck6fTY18TZKEsQhGtb14l+7DUDrHog/4Kr9e/99H/w+YN84/OfQKORkWWZzzz6fr7y1w9jD/fylR//iJ5+0ZY/XbN10/Y3wPZ3bPsK8IaqqkuAN8a+FwThBqYoKod+NzbIatvEK/U39lXx5tvVfPJD95G7yDbhuffcdSsfWXY3g+5hHv3Ct2lobp/HWl8/ZiXwVVXdA7zzjsq9wONjjx8H7puNsgRBuHad3t3FUOcoax8qnDDIatjl5l9//DjFBTl8+D3vmvTYm29ayT0xtyFLEp/6u39i76Hj81Xt68ZcdstMU1W1e+xxDzD5qApBEG4I0UFWp0ldkkDOuvQJz/37z5/E7fHyzS98Aq1GM+nxGSVWkjQJfOsjf0VOVgZf+scf8scXXp+Pql835qUfvqqqKqBO9pwkSZ+SJKlakqTq/v7++aiOIAgLoOalNrzDAdZ/aOIgq90Hj/LqrgN87MG7KcjNvujxybkWdCYt/o4QP/+3r3HTupV876dP8P1fPElErIo1JXMZ+L2SJGUAjH3tm2wnVVV/oapquaqq5SkpKXNYHUEQFop3yM/JF9vIXZ9OWuG5/hsjbg//8l+PsyQ3m0fef/clzyFrZDKKk3DUDWIyGvi3r/8ND917O79/7jW+/N0f4fMH5vQ1KKrKB5/u4BPPd3Goy0v0OvbaMpeB/2fg4bHHDwPPz2FZgiBcxY48fRolrFwwyOoH//07hoZH+OYXPoFOd/le4rbSJEZ6vYwO+NBoZL74Fx/iS3/5YfYePsZffPmfGXDO3TTKzc4gpweDNA4E+MxLDj7xvP2aC/7Z6pb5e+AAsFSSpC5Jkj4O/AuwTZKk08DWse8FQbjBODvcNO3qouT2xVjSYsa3768+yYs79/HR991FUUHOlM6VUWoFmDDNwoP3bON73/gcbR12Hv3Ct2lu75rV+p9VbfcB8Pv3LuIrm1Po9YT5zEsOPv68nYOd10bwz1YvnQ+oqpqhqqpOVdUsVVUfU1V1UFXV21RVXaKq6lZVVcW4aEG4AR3+fQM6k5aV958bZDXq9fFPP/o1udk2Pv6Be6Z8rqSsOIxxOrrfMc3CzetX8d/f+zrhSIRPfOk7HDpae5EzTF+V3cuieB1Z8TreUxLPMw8t5iubU+jzhPnsy9dG8IvJ0wRBmDNdJ/vpOjHAqvsLMMbqx7f/6LE/0D84xDe+8HEMev0lzjCRJEtklFhx1A1eEKxFBTn8+gffJCPVyuf+4fs8/+ruWXsd4YjK0W4fazNN49v0Gmk8+L96fvA/18WBTs9VGfwi8AVBmBOKonL4d43EpZgouf3cSlZVx0/xzCtv8YH77qCsqOCKz2srteJx+hnp8V7wXHqKlf/+979n7coSvvP/fsWPf/MUijLzHjx1/X68IZW1meYLntNrJB44P/i9Ef7m5e6rMvhF4AuCMCea99hxdrijK1npon3rvT4/3/nPX5FtS+MvP/zAtM5rm6Qd/3yxZhM/+IfPc/+7tvCbP77I3//bzwgEg9N7EWOqHdH2+zU200X3ORv8z44Ff/9VGPwi8AVBmHUhf5jqp5pIKYgnd/25QVY/efxpHD39fOPzH8doNEzr3JZ0M+YkwyWXPdRqtXz1M4/wNx97kNf3HOLTX/1Xhl3uaZUHUGX3UWjVk2CcfFDY+XTvuOI/G/wfe66L/R0LG/wi8AVBmHW1L7fjHQqw/kPF44Osjtc18ccXdvL+u7eyatnSaZ9bkiRsJVa6TzlRlYuHpyRJfOS9d/IvX/sMjS1nePT/fJszXT1XXJ4/pHCyxzdpc86lvDP4B7wRPvfKwga/CHxBEGaVdzjAiRdayVmXRvrS6CArfyDIP/7gl2SkWvnrR9434zJspVb8I0GGukYvu+9tN63lp//yFTxeHx//4rc5Vtt4RWWd6PUTUphww/ZKnB/8X7s5hcGx4H/0uS7enufgF4EvCMKsOvr0aSJhhbUPnbuK/8UTz9Lh6OXrn/sYZpNxxmVklIy141+iWed8ZUUF/Or73yQxwcJff+3feOWt/VMuq8ruRSPDqozpBf5ZOo3E/cXx/Gks+J3eCJ+f5+AXgS8IwqwZ6nLT+FYnJdsWEZ8eHWRV29DCk8++wn3bK1m3snRWyolLMWFJM1/ROrdZGak89u/fYHlJAd/83s/55e+en1LIVtl9LEs1YtbJKK4h1FBoJlWfEPxfn+fgF4EvCMKsOfy7RnQmLavuj3a3DIZCfPuHvyQ5KZHPffyhWS0rY6wdX7mCidMscTH86B//ljtv3cjPn3iGb/3gl4RC4Yvu7w5EaBgIsNZmQo1EGPjMw7gf/9lsVB+dRuK+84J/yBcN/keenbvgF4EvCMKssNcM0Hm8n5X3F2CMiw6meux3z9PW4eBrn32E2Jgru+l5ObbSJEK+MIPtI1d0nE6n5f9+8VN86kP38dLOffzNN/6dEbdn0n2PdvtQVFibaSbc0YYyPIR/12uo4Yu/SVyps8H/9IPR4B/2z13wi8AXBGHGzq5kFZtionRskFVjyxkef+ol7tp6E5vWrpj1Msfn1ZliO/75JEnikx+6n2996VMcP9XEx7/0j9h7Lpyevcruw6CVKEszEqqvAUBxDRM8cWRmlZ/E+BX/g4v5+8rU8eB/+Nku9p2ZneAXgS8Iwow177PjPONm7UPRQVahUJhv/eCXJMTH8YVPfmBOyjTHG0jMisVRN/1puu68dRP/9d2/wzk0wqNf+Ba1DS0Tnj9s97Eq3YhOIxGsr0VOSEKKicW3e+4WXtFqJO4tsowHv8sf4Qs7Zif4ReALgjAj4UCE6j9GB1nlbYgOsnr8qZc43drBVz/zCPFxsXNWdkaplZ5GJ5Hw9KdPWFNWxGPf/wZmk5G//Mo/8+a+KgAGPGHahoLj/e9DDbXoSsowbtpC4OAeVL9/Vl7DxZwf/N+YpeAXgS8IwozUvNKG1xlg/QejK1k1t3Xy2P8+z+2V66msWD2nZdtKrESCCv3NM5sHPycrg1//4JsU5i/mK//8Y/7nTy9TZY/O1bM200RkaJBIjwN98TKMldtQfT78VVPv2jkTWo3EPRcJ/r1XGPwi8AVBmDavK8DJP7eSszaN9KIkwpEI3/7hY8TFmPnbv/zInJefUZwE0vTa8d8pMd7CT/7py9y2qZz/fOwPPPabJ4nTQaHVQKihDgBdURn60hXIScn457BZZzLvDP6RQIT/s6Obh5+Z+vz/IvAFQZi2o0+fJhw6N8jqd8/soP50G3/7Vx8hIT5uzss3xOpIzrHMSuADGA16vvuVT/Pw++7izPGD6Kr+iN/vJ1hfAzo9uvwlSBoNxptvI3DkIIr7ynoIzYazwf/0+8eCPxiZ8rEi8AVBmJboIKsuSrYuIj4jhvZOBz9/4lm2bFzD1s3r5q0eGaVW+k4PEw5MPfguRZZl7nvgflhxF0Odp/nk334XR81JdAVLkXTR7qamym0QDuPfv2tWypyO84N/qkTgC4IwLYd/34jOqGHV/QVEIgrf/uFjGA16vvzpj45PmDYfbKVWlIhKT9PQrJ2zyuGFnNV8/e8+h6N3gM81jnImPXf8eW1+IZrMRfh375y1MqdLq5n6z1oEviAIV8xeO0DnsX5W3puP0aLnDy+8Tk19M1/8iw+RnJQwr3VJX5qIpJHonqVmHYj2v08xa7hn8wp+9tcPIgOf393AvsPHgWg/flPlVoK1x4kM9M1auXNNBL4gCFdEVVQOP9lAbLKJkjsW09Xdy08ef5qb1q7gXbdunPf66IxaUvMTZq0dX1FVqu0+yjPNSJJE9nAv/yHbWZyVzhe//UPefLsaAGPlVlBV/HvfnJVy54MIfEEQrkjzPgeDZ9ysfagQWSvxjz/8FVqNhq989pF5bco5n600iYFWF0HvzCY2A2hxBhnyR8anQw7W15KSkcYv/uObLM7M4H+efhkArS0b3ZLiOR2ENdtE4AuCMGXjg6zy4snbkMEzr7zF0ZoGvvDJD5CWnLRg9cootaKq0F0//VG3Z1XZo8sZrs00oaoqoYYadEXLMBkN3L1tM7WNLXR19wLRq/xwSxPhzjMzLnc+iMAXBGHKal9px+P0s+5DRfT0D/KjX/2RdStLuef2mxe0XqkFCWh0Mt2nZiPwvSyK15EeqyPS240y5ERfvAyAbZXrAXh110EAjJtvBUnCt2fhb95OhQh8QRCmxOsKcOLPLSwuTyO9KJHv/uevUFWVr3/u0QVryjlLq9eQVpg443b8sKJyrNtH+dhi5aGGWgB0xWUApKdYWbVsKTt2HUBVVTRJyeiXr8a/e+dVsUj55YjAFwRhSo79qZlwSGHdB5bywut7OXSsjs9+7EFsaSkLXTUg2j3T2eHGNxKY9jlO9fnxhNRz8+fU1yKZTGgXneuSuX1LBe2d3TS1dgBgrNxGpLuL8OmGmb2AeSACXxCEyxqyj9LwZifFty0ioAvwg//+PavLinjPnbcsdNXG2Uqj9xBm0qxT7Yi235+9wg821KJbWoqk0Yzvc+tNa9FqNezYdQAAY8XNoNVdEzdvReALgnBZVb9vRGvQsPL+PP75vx4nFA7z95/7GLJ89URIcl48OpNmRs06VXYfhVY9CSYNis9LuL0FXdGyCfskWGKpWFPGa7sOoigKcmwchrUV+Pe+gRqZndG+c+Xq+W0JgnBVctQN0nG0j5X35rHryBH2HT7Opz/6HrJtaQtdtQlkjUx6UdK0r/D9YYWTvX7KzzbnNNWDoozfsD3f9i0V9A0OcayuCYhOtaAMOQnWHJv+C5gHIvAFQbgoVVE59GQDsclG0tcn8h8/e5Ky4gIevOf2ha7apGwlVlzdHjyDvis+9kSPn2BEZV3meTdsJQldYckF+25evwqT0cCrY806hvIKJHPMvM+geaVE4AuCcFHN+x0Mto9Q/v5Cvv/LJ/EHgnzz8x9Ho7k6o8N2dtnDaVzlV9t9aGRYlXFuwJV2US5y7IWzfpqMBiorVvPG3ipCoTCSwYCx4mb8+3ejBqd/03iuXZ2/NUEQFlw4GKH6D00k51poU7p48+1qPvXh+8nJti101S4qaVEchljdtNrxqxxelqUaMetkVEWJrnBVdGFzzlnbt1QwMurhwNHoWrfGym2oXg+B6oPTrv9cE4EvCMKk6na04xn0U3RfFt/76f9QXJDDhx7YvtDVuiRJlsgoSaL71OAV9Yt3ByLU9wfGe+dEus6gekYnbb8/a/2qUhIscex4K9qso1++Cjkh6aruraNd6ApcLVRVxTUySld3H/aePto7HbR2Ohh2uSkpzGPtihLKivKJi41Z6KoKl6EqKiO9XgbaXEiyRGpBAjFW44IPDrqW+EYCHH++hUVrUnnizZdxe7z85J++jPa87olXK1uplfbDvbj7vFjSpvb/9Wi3D0VlvP99sP7sgKuLB75Wq2Xr5nW8sHMvHq+PGLMJ4+Zb8e74M4pnFDlm7tbyna4bKvBDoTDdfQPYe/qx9/Rh7+mno7OHjq4eegYG8QeDE/Y3mQzodVqO1TbyxJ9eQQIyM1JYuayIVaWFlBUVsDgr/arqmnajUVUVd5+PgTYXfS3DtDU6aGrtoM/rZCg8gIxKgiaZjIRUipfmkl9qI21JIsl58Wj1V394LZRjf2omHFAIF/l59ccH+dSH76cgN3uhqzUltpKxdvw655QDv8ruw6CVKEszAtEbtpIlHk1G1iWP275lA0+/9Aa7Dx7lzls3YazciveFp/Ef2IN5650zeyFz4LoKfFVVcbk90TDv7qeru5czZ3rocvTh6OtncMQ14WOeRpaJjTURF2cmNzeDuBgTFm0sSbp40mKsJJqTCEtaeu1d2EM99IYG6XcO89rug7z4+l4AYswmlhcXUFZcwPLiJZQuzSPWbFqoH8F1TVVVRgf8DLS56Grsp/5UGy1nuuj1OhmMDDMUGSJAeHz/VEKoQGO4G3rhuV6I2wMZso40TQyZiankF+RTtG4FthWLiEs1i08BwLBjlPo3Olm0OYXv//63LMnN5pH3vXuhqzVl8bYYzAkGHHWDFN06tTepKruPVelG9GOLiQTra9EXLbvs30NZcQEZqcm8uusgd966CV1hCZp0G/7dO0Xgz4ZwOExP/yD27n7OdPbQ3tZNp6OX7r4Beoec+EMT75CbtAbizGbiLWaysq3ExpmxxJmxmGOwmhKIN8YRb4lHjUvAbYzHEZbo6ndzxt5NpL8Hc8BDT3IOxrSVJPtCLO7vxZQziF/npD8wSN/AMA0t7Rw8UoNKdGGE/MWZlBUXUFYUfSNYnJkuguQKqaqK1+mnp3mYxpoz1Ne30Wp30OcbxBkZZkTxjO9rkiFHCrFK8pFDgFyzjtTcHIbTcwjozRi9LlyDgzicLlqGRmnzBTgYChDqG4a+JrT7XyaLEItkyDaZyE1OomjJIjJL89HZbGhS0tBYU5B0uoX7gcyj6CArmb3D1QwNj/CD//sFdLprJyokSSKj1Iq9ZgBVVS/7f2/AG6Z1KMidhdFPBoprmIi9A9Nt77psWbIsc3vlep740ysMuUZIjLdgrNyG56n/ITI0iCbROiuvabZclb/FEbeHTkcvba0O2tu76bJHr9B7h5wMeUZQOe8qHZk4TQxxRjN5STYsSTHEWY3EJBiIjTWh02oxag0kWeJJTEpCNVlw+WWGup24HT0MtbZicPaR6hsgxTdIkXeAhOCFCxN7DbHUJ+RzKiGf2sQCWmPXY41VyTIPsKzAjcE4wuCIk76BYYaG3Lzy1n6efWUXAPGWWMqW5o+/CZQuzcNsMs7Xj/Oa4Bny01br4OTxFppOd9De7aDP72Qo4iJMdPSiBGQYDZQZI+T6RshRvOQQJCEpif6MHLoSM+hOSKYm1ohWPu+GXZIFsrJRVLBKehJlAzeFI0hDTvyD/Tj7BnEMujjp8bPHEwFPP5zpx7rzELkEyZUC5BAi12JkcUYK+tR05JS06BtBSurY1zQkS/w1/8beXT/ImSN9mDbKvPLyfh598G6KCnIWulpXzFaaRMvbDobtoyRmXXox9erzpkMGCDbWAVzyhu35tm+p4PGnXmLn3ire9+7bMFVuxfOHx/HvfYuYe947g1cx+66qwG9u7WLz3Z/CH3nHVbpkIE6OxapPoNC2mMTkOOKTTZgTDcgmlQgKEH1nT7DEkWAwovcFiQx7CPS5iAycQTvYh+zuJ9U3QFFwdML5Ixod4aQUNJkZxNhK0aWno0mN/pNMZkItjZjqa1lXX8uaxmcAUCSZHutiaiz5nIzPpz4xn2DcMgrihllZ4CLB6GFkZJS+gWFG3X5aznSxr+oEALIskZ+TzfLiApaPfQrIyki95sNiqlyDHk4ebqa2poXTrV109vfQ73fiUc8NlonRGclJiKHCGE9eaIjFTjvZSgB9GFwJmXQvzqMvMZXqFCsBc7SdNqhqCOviMMfEk56cyFJbMkadjo7BEXqGRxhye1C9HpSgD58UxphkxGjNxlaYjQ0oB0b8KoMuH+5BF+7eAdqHhjjq9Ub/wlygdwXIbmolnzpyVT+5UpAcAsRIKugN4+F/9o1APv9NITkVSW9YiB/5lEQHWTWiTZT53aFXyM228YkP3rvQ1ZqW8f74dYOXD3yHjzi9zFJr9HcTaqgFjQZdQdGUyirIzSZ/cRav7jrA+959G9rsHLR5S/Dvfk0E/qXIqsQyawFpSVbS05KwpluwpJiI6COM+N0Mj7pRFAVdMECcf4TkgIJp2I/GPYo0PIx+eJC4kQFiQ54J5w1pdIzGpxJOSUVJKyGcaSMxO3M82OWEJKRL3HjV5RbA1rsAUNwjhBpPEWyoxVhfQ2bT29zRGp0L2xeXRFtiPsdi8ziUWMBoUj7pizzkaVys1vgIBEIMDnvweoI4B4d55c39/Oml6PJoifFxLCvKZ3nxEsqKCyhZkovJePWGw1SoqkrXmT6OHm7i1Kk2Wju66HL24Qy6UMbepGVk0ixJrEzLpNAiswQ32T0txA+0Ig1DWKujLzmTwZLlHE5NY8iaRlinx4cBDHFYLAnkpSRRkpVCtjVu0jfN/PTJ11h1+8O0DYzQOTBCr2sUl9uDovMSb/RhTUkkptiGRlKJRBSGXaM4h9wMOKNfdw25eC0U4uyHTateT55Rz5KwRH7PCIvb2kgZHkB+R3XkhEQ0aTa0OXloc/LR5eSjzcmfdHDPfGs50M1Aq4vm3FYGzgzzr1//LPprtBkrLsVMXIoJR52T0jtyLrlvld3LGpsJjXyu/V6XV4hknPqn8Du2bOAnjz+No7cfW1oKpsptuH/9E8KOLrS2S9/4nU9zHviSJG0H/h+gAX6pquq/XGzfNFsS9zy6AVdPN0p/J7oeN1KLm0S/hyyfD8PoCKaRYfTBicOm/Ro9A+ZkXPGpDGUVoUtPJy4zk9ScTFIXZ6JJTJq1q2c5zoKhfAOG8g0AqJEw4fbWsTeAWmLraynpqOJDgKLR0ZeQQ4Mph9qkHHozM7AkKWSmjFCQl0r5miUMeRRGvUFGh0do73Sw91B0kWSNLLMkL5uyooLxNwFbWvJV8ylAVVUURSUcCROJKITCYVpb7Jw40kxDYzttdgcOVz9+5dyntTht9EbpuswiilONFBu8pDhaUBvr0HRG36R9xhgGUtI5s3oTgykZOBOS8Wnj0JosWBMSKU5LomxRCgkxM28SizNqWZ6VxPKsC1dqiigqvaMh2gfcdDndyMOjaD2jWPw+8kI+9IofOeBiZGgE57Ab55CbpiE3VY6znx4t6LRJ2JISKExLYGmimUKzhjzFj9TniI7IfPWF8fLk5FR0OXloF0ffALS5+WgzFyFp5+eaLDrIqhG31c0bxw7z4fe8i2VF+fNS9lzJKLXSfrgHRVGR3/nOO6ZrJITDHeZDy6MXBWo4TKipHvP2e66orLOB/+qugzz64N0Yb74N929+in/PTmIfemSmL2XWSHM5ab8kSRqgCdgGdAFVwAdUVT012f7LrAnqK5Vr0IUmdo/0aoz0ma30mZJxWVJQrGno0zOIz7KRmpPF4qxkkmO0V00YRgYHCDXUEmyoJVRfS6ilEcLR3iOjhmTaDdm0Jy+mJyuTUKqZZK0XjaQSVmUGQ0ZGPEGC7lFcfQ462s/g80dDMykxnuVF+ZQU5mE0GMbDNhyOEFEi0a8RZeL2SIRwJEI4ohAJhwkrE7dH9xvbPxLdfv7jcDhCWBk7NqIQDoWJKNHnL0aLhhRjEtnJaRTkZrMsP43ShCC0NOCtPYGpqxWNEj1+xJLAYEoGg8kZ9CRn4UzIxBQbT5o1iUKblZJMK3rt1dd90h2IYB8JcWbQg8M5woBrFNeIG5/Hjd/VR3B4AK9riOEhF0PDbkKhcz+v2FgzcTExJJqNpOtlUgmTEvJiHXWS4OwhSQliIYJGp0WbtTj6SSB37I0gJx850Trrf+snXmhl/+9qeUnzFgajjid//B2MBv2sljHfmt92sOvHJ7jvOxtJzoufdJ9n6138055+/vj+ReQm6gmdbmDw/3yS+L/7FqbNt15ReZ/40ncY9fj4359+F4DBr34WZchJ8k+fmPNskiTpiKqq5Zfbb64vH9YBzaqqto5V6n+Be4FJA98v63kl82b6zMkEElMxpmdgybKRmZFEXpKBmxP1JBivvv/876SxJqPZtAXjpi0AqMEAoeZGQg21GBrqiKs7ybLWY9AKIclAvz6T7uRsBnKzkFITSEtUIdHIaFYR1mXlDPtVVM8oynA3dS1n2HXg6KTlarUaNBoNWlk+91gz9lWrQaORz32vGfteq8Vg0KORZSRkCKuoYVBDKmoIIgENqha9AAAgAElEQVQFJagSURTUiIoky8iyhKyVkWUZo0mPMVaHOdZIZlYKy5fnU5CpZ6D2GKMnjmGuP4Rlf3T9T0WWCSSl4Cgsw5GyiD5bIfoUG1kpSZRkJXNnctw1M6YhzqChKEVDUYoRmNgTIxxR6fGEae/z0tgyRGfPML2DPQy7evGPDhDyDBPye+kadNLsDxAMnu1KKgEZ449iQxIJbQrJrXVYOUYiYZKIECdrMOoSMBhT0JltRGIzCcSmg86IRisjayQkTfR3pNHIyFoJWSMha+To700jjW2Tx7efeKGV+rhm+uxD/Pxfv3rNhz2ArST6yc1RN3jRwK+2+0g2a8hJiDZdnR1wNdUbtue7Y0sF//aT39Lc1klBbjamym2M/Ph7hFtPo8svnOarmF1zHfiZQOd533cB6y+2s5SWyaZvfJnFCXpi9dfGf/ypkPQG9CXL0ZcsJ4Zoc0ikt5tQfQ3+2pNoTpwgw74HyR7tf+TWpuDKWMRAdiZp1gQ88TGo8RL96YuxFywHv4GgEg1cSZLQaGQ0soRWktDKoJFBlog+lqL/ZAlkRUVSFCRFhYiCpCioYQXCCuGwgqyqSKqKrKrIqOhQMMkhzFIEgxTCJIUxyGGMagSDFMZACF04iDYcQBt0YWo9TNL+dvy+UWIBvU7PYHI6zcs3M5hViJJfwqKsDMoWJXNz7PU7VkGrkciy6MiyxHNTwbmgUVUVV7eHrq5R7B4FuzdEjz/AgGcE9+gQPu8wcsCF7B9F9Y8SDvjw+gJ0+AI0ev34AkFUFYiM/fMPwPAAJo6TRJh4WSZOayBGF4tRn4DekIxOZ8UsmzBhwqDqUBVQIgpKWEUJK5z9gN8nDXJg6AQP3rONVcuWLsSPbdaZE40kZMbgODXI8rvzLnheVVWqHT7WZ50bfxFqqEFOTkWTnHrF5W3dvJb/+NkT7Nh1gM/kZmPcWMnIz3+Ab/frN0zgX5YkSZ8CPgWwaNEiSlOv/+6KkiShTbehTbdhuuUOEgHF6yHYUIdzXzXakyfI6Kolu/MIAEFtDJ6MRQxmpGO3JDCUmIKi0aAJh9FGQmjCYTTBMNrw2OPIJI8j4ej+4TCasWO0kTCaS+wnq8oVvS6POY7utEUMZRWiFC4no2wZyxalsEq/4H9mVwVJkkiwxZJgi2Wy60dVVRnwRuhwheh0BekY8tMz5GbIPUrA78Gk+jCGRtD6R5ACbgI+PxHXCOGREQKjXnyBIB3BIMPBIXweF3Bmwvk1soQ1LgZrchLJyUlYE+NJTkwgMd7Cy38+ic2Qwqcfvrp6lcyUrcRK0x47kbCCRjvxIrLFGcTpi4zPnwMQbKib1tU9QGK8hfWrl/Ha7kN8+uH3IlviMaxej3/PG8Q98leX7BgyX+b6f6IdOH+oW9bYtnGqqv4C+AVAeXn51b8K8ByRzTEYV6/DtnodAEokgrOqjoG3DhOqr8HU1U5BZz0FgIqExJX/qFSNFlVnAIMB2WgEgwHMFiSDAfRGJOPZr0YkvQHJEH2MwYBkNCHpx44be6zqjUQMRlSdEcVoICc+jvyr4I/6WiVJEikxWlJitKyxmYB4ILrISERR6fWE6XSF6HCF6BgK0O3y4HKN4vd7iJUCZMh+4uQAicooCc5u5P5+lKEhIq4RAh4v7rCKc2SEIVcfne0GaiQtrrA6PmDwv777t9fd+JCMUiunXu+gv8VF+tLECc8dHut/f3b++8hAH0p/L7r7Hpp2eXds2cA//PsvqKlvZkVpIcbKrQQOv02o7gT6slXTfyGzZK4DvwpYIklSLtGgfwj44ByXeV2QNRqSNywnecNyAEYHfXTua8a5rwqlrRlZK6ONNaO1mNHFx2JIiMGQFIcp2YIpxYLJGodkMiEZDEgGE5JBj6QRV9rXKo0sYYvTYYvTsf4dvfzCERXHaIiO4dDYp4MQ1Uk+BlPdBP1eYqUAcZKf9OAgy0bspLl6iB8eIH54ELNrGLcqEQZy4679dvt3yihOAgm66wYvCPxqh5dsi470uGj7fajhygZcTaZyw2oMBj07dh1kRWkhhnWbkIwmfLt3Xv+Br6pqWJKkzwCvEu2W+StVVevmsszrVazVRPG9ZXBvGaqiIl2km5lw49FqJBbF61kUf2FgB8IKXSOh8U8G7a4Qe4YD9I94CAe8JKgeiodb+Niex2j68zOs+cKXF+AVzB1jnB7rYguOU4OseqBgfHtYUTna7eeO/HMzWgbra0BvQJtbMNmppiTGbKJywyp27j3MF//ig2iNJgwbbsL/9ltY/uLzCz49x5xf8qmq+jLw8lyXcyMRYS9MlUErk59kID/pwkF8nmD0zaDFuZK+4y+gqz2Gy+0mPm7hB4HNJluplbpX2wkHI+MzpNb3B/AEFcozz7Xfhxpq0RUWz3jswx1bKnht9yEOHatj09oVmCpvx7/rdQJHD2Fcf9OMzj1TosFVEG5QMXqZpckG7iyMoz1/Lcn93Rw+fHihqzXrbCVJKGGV3qah8W1Vdi8A5bbo/PdqIECopWlGzTlnVawuwxIbw46x9W71K8uRLPFXxXq3IvAFQcBWuQVZVfBUHaDfeeXrwV7N0oqSkGQJR92511Vl97HEqifRFL3iDzU3QCRyySUNp0qn03Lb5rXsPnAUnz+ApNViuulW/IfeRvF6Z3z+mRCBLwgCK29eg0dnJq27g4PHTyx0dWaV3qQlJT+e7rF1bv1hhZO9/vHVreC8AVdLS2elzDu2VODzB9h76BgAxsqtEAwQOLR3Vs4/XSLwBUFAq9MxXLiaZHsnnd3ddPX0LHSVZpWt1Ep/q4ugN8TJHj/BiMpa28T2e01mNnL85BPtXalVpYWkWhPHm3V0RcuQU9Px7d45K+efLhH4giAAkFW5mbjAKPEuFwePn7iiRcCvdrZSK6qi0tM4RJXdh0aGVRnRwFdVlWB9DfpZaM45S5Zlbt+ygf3VNQyPjCLJMqabbyN4rArFNXT5E8wREfiCIACQsrECFQnODNDndNLa2Xn5g64RqUsS0OhkHHWDVDu8lKYYiRmbviXS3YU64rrkguXTsX1LBZFIhDf3VQFgrNwGSgT/vl2zWs6VEIEvCAIAcnwigZylZNtPozfFcujESRTlyqbXuFpp9RpSlyTQdsrJqf7A+OpWAKGx9ntdUdmsllmYt4jcbNu5Zp2cfLSL8/AtYG8dEfiCIIxL2riJQlcbXd4Eht1u6ltbF7pKs8ZWaqXOHUFRmRD4wfpapJhYtNmLZ7U8SZK4Y8sGjtU20tMfvWFsrNxKqL6GcG/3rJY1VSLwBUEYZ1pbAYCvvoXERCtVNbWEwuHLHHVtsJVasSeY0EtQlvaOAVdLS+dkcrM7tkQXSnp99yEAjJtvA8C/541ZL2sqROALgjBOm18IiVbW9Z2k35iL1+ejprFpoas1K1Ly4nEkxZCLgl4THa2ujLoJd7TNyoCryWRlpLFsaf54s4423YaueBn+PbPXrFO/s2PK+4rAFwRhnCRJmMo3UD5Yy8vtKtkZNo6eOoU/ELj8wVe5oaCC06wnvX90fFuo6RSo6qzfsD3fHVs20NTaQWtHdKJgU+XthNtbCbW3zPjco4M+Dj3ZMOX9ReALgjCBobwCQ9BHhqMJrAUEQyGOnpp0kbprSrUjOh1yUscwniE/EJ3/HllGt6Rkzsrdunkdsizx6q6DABhv2gKyZlamWjj42/or6j4rAl8QhAn0K9eCVsstwzW8dAYKc3KoaTrN6AJPCzBTVXYfsVqJZHdgfNRtqL42uk6w2XyZo6cvOSmBtStKeHXXAVRVRY5PRL9qLb49b6DOoBdU54l+2qt6WXXf1Gf3FIEvCMIEstmMvnQFFYM1HHH4yMwpQlVVqmpqFrpqM1Jl97Im04wpRofjlBM1EiHUWDerA64uZvstG7H39FPbGG3GMVVuRenrIdRQO63zhYMRDvzmFPEZMZTdlTPl40TgC4JwAUN5BXH9nWT4B3j1TIRlSwpoaG3D6XItdNWmxT4SwuEOsy7LREZJEo66QcIdbag+75y235+1ZeMa9DrdeLOOYf1m0BvwT3OqhZMvtjLS62XjIyVodJopHycCXxCECxjKo90zHwzV82LTCMuWFqPVaDh04uQC12x6xqdDzjRjK7Uy2u/DXXUUYFZmyLycWLOJzetX8vqeQ4QjEWSzGeP6Tfj2vYl6hd1eR3o9nHi+lbwNGWSWJV/RsSLwBUG4gCYzG01GJhUDNbj8Cod6IqwsLqatq4uegYGFrt4Vq7L7SDZryE3QkVFqBWC0+hhyYhKatIx5qcMdWzbgHB6h+kT0BrixchvqiIvg8eopn0NVVQ48Xo+skVj/4aIrroMIfEEQLiBJEobyCsynT5BjjvDMKRcri5ZiMhquuYnVVFWl2uGj3GZCkiQSbDGYEgzQ3oCuqAxJmp8V5DaWLyc2xsyOs806q9cjxcZd0VQLZ6r76Dzez+r3LCEm6coXnBeBLwjCpAzlFRAM8ojuDEe7/dhHVcpLl+Ho66Oje2GmBpiOlqEgTl9kfP57SZLIztei9w2gK5q77pjvZNDruXVTObversYfCCLpdBg3bSFwcC+q33/Z40P+MAd+e4rE7FhK75jeNBAi8AVBmJS+bCWSwUh53wk0Mjxb76KkIB9LbOw1dZVfZY/2vz9//pxMS7RZKpA8/QXLp+OOLRvw+Py8XRVdZMZYuQ3V78Nf9fZljz3+XAueQT+bHi1F1k4vukXgC4IwKUmnj67HevwQWxabeanJTViVWLe8jMHhYU63n1noKk5Jld1LlkVHRpxufFtCsIsIGnpHE+e1LmvKiklOSmDHW/sB0JeuQLam4N916WadIfsoNS+1seTmTNKLkqZdvgh8QRAuylBegdLfy/vjh3EFFN5s87Bk8WKSExM4fPIkkUhkoat4SWFF5Wi3f8LVPQBnGhg12HA0uue1PhqNzO03r+ftqpOMuD1Isozx5tsIHDmIMjJ5l1dVVdn/6zq0Bg3rPrB0RuWLwBcE4aIM5dHZHpd0HiPLouPZUy4kSWLDihWMeDzUNc98Ppi51NAfwBNUJgS+GgoSam4kkllI9yknqjK/TVN3bKkgFA7z1v5o7xxT5TaIRPDv3z3p/q0Huuk+5aT8wUJM8YYZlS0CXxCEi9Ikp6LNLSBYfYD7ii0c6/HTOhQkOyMDW2oqR2prCYZCC13Nixrvf287N3VCqOU0hIKYlq8g4Akx2DEyr3UqXpLDIlvaeG8dbd4SNFmLJ+2tE/SGOPREA8l58RTdtmjGZYvAFwThkgzlGwjV1/LuTNDK8Fx99Cq/YuUKfIEAJxqmPlvjfKty+FiSpCfRdG40aqghOkVE8q3rAHDUOee1TtGFUSo4crKe/sGh6AyllVsJ1Z0g0t87Yd+jf2rG6wqw6dESZHnm3UdF4AuCcEmG8gpQIpgbjrIlJ5aXmtz4wwppycnkZWdxvL4B7xS6Fc63QFjhZM+F7ffB+lo0aRnE5WYSb4sZn0htKlo7O3H09c24h9LtWzagqiqv7xlbGOXmraCq+Pe+Ob7P4JkR6na0U3RrNin5CTMq7ywR+IIgXJJuaSlSnIVA1QEeKLEwElB4szU6p/z65SsIRyIcqa1b4Fpe6GSvn0BEHe9/D9EboKH6mvH5c2ylVnoanCjhy89aWdPUxI69+3hu5xv84ZVXqD19mtA0m7NysjIoLsg516xjy0JXWDzerKMqKm//ug5DrI7yBwunVcZkROALgnBJkkaDYfV6AkcOsjrdQLZFxzP10XbvxHgLRXl51DU3MzI6epkzza8quw+NBCszzl3hR/p6UIac6McWLLeVWgn5I/S3XnpSuOaODvZWHyEnM5Mt69YhSxJ7qqp5/Lnn2Vt9hCHXld8H2H5LBfWn2zjT1QNE++SHW08T7mzn9B47fU3DrPtAEcZY/RWf+2JE4AuCcFmG8grUEReR5gbuL7ZwosdPizO6CtbasmVIksThk1fX9MnVdi8lqUZi9ediLtQQ/SRy9go/ozjap91x6uLNOvbeXnbuP0B6cjLbNm2kpCCf923fzv3btrI400ZdczO/f+kl/vzGm7R2dqJMcY77bTevR5IkXh1b/tB40y0gy7h3vsrh3zeQVpjIkpszp/XaL0YEviAIl2VYvQ5kmUDVAd691IJOhmfHrvJjzWaWLy2kqb2dgaGhBa5p1GhQ4VR/4IL2+1BDDZLJhHZxLgDGOD3WxXF0X+TGbb/Tycu79xAfF8udlTej02qB6I3XjJQUtm3cyEfvvYd1y5cz5HazY+8+nvjzC1TX1uH1+S5ZxxRrImuWF48vjKJJSka/fDXuV18lMBpi48dKkGbhRu35ROALgnBZsiUe3dISAtUHSDRpuCU3lpfHbt4CrCopwaDTcfAqmT75qMNHRIV1k9yw1RWWIGm049sySq30Ng0RDk4cROYaHeXFXbsx6PXcfcstGA2T94E3m0yULyvlI/fczfbNN5FgiePwyZP89vk/8/rb++nu77/oTd7tWzbQ4eil/nQ7AKHiTeg9/axaq2JdZJnBT2ByIvAFQZgSQ3kF4ZYmIs4B7i+24A4q7GyJttsb9XpWl5bQ4XDg6Otb4JpG+98bNBLLUs/NKKn4vITbWi6Y/95WaiUSUug7PTy+zev38+Kbb6EoCu++ZQuxU1gCUZZl8rKzuefWW/nAXXdRWlDAGYeDZ1/fyVM7dnCquYXQO+a+v2VjOTqtlld3HUBRVA6fTCSCllx94wx/Ahep45ycVRCE645h7UYAAkcOscZmYlG8brxZB6CssJAYk4kDx44v+MRq1Q4fK9KNGM6bZCx0ugGUCPrisgn7pi9NRJIlHGPdM4OhEC/t2o3H5+OuykqS4uOvuPzEeAuby9fw8H33Url2LYqqsuvwYX777HO8feQowyPRKR0scTFsWruc1/Yc4tTr7fR2BFGXriF0YBdq5MoWRpkKEfiCIEyJNicf2ZpCoOoAkiRxf7GFk71+msdu3mq1WtaWldE7OEhbl33B6un0hWl2Bid0x4ToguUAuqUTp0TWm3Wk5MXjODVIJBJhx969DAwNcftNm0hPubIVpd5Jp9NRuqSAB9/1Lu7behvZGRnUNDXxuxdf5IW33qK9y87tlRsYcA7z/BP7sJVasd7/bpRhJ8GTx2ZU9mRE4AuCMCXRRVE2EDxehRoK8e7CsZu3p85d5Rfl5ZJgsXDoxIkp91aZbdWTTIcM0Ru22uwc5Ni4C47JKLXS1zLMzrcP0NXTy5Z168jJnL0eMpIkYUtN5fabNvGR++5lbVkZzmEXL+/ZQ1e/A71WR4O3hY2PlGBcW4FkjsF/BQujTJUIfEEQpsxQXoHq8xI8dYIEk4Zb82J5+bQbfyga7rIss37FcoZGRmhsa1uQOh62+4jVyxQln7vJqioKwYa6iy5YnlGSSHipl5auTjasWEFxft6c1S/GZGJt2TI+fO893HHTJmJ0JhYtSqE13MnB1mP0jYxi3FiJf/9u1EBgVssWgS8IwpTpV6wBrY5AdXSE6P3F8YwGFV5vPTfoKi8rizSrlcM1tYSvcIHu2VDt8LLGZkJzXpfGiL0DddR90cDvUXtR8oKkqimsKimel3pqZJlcWxaat2NYphQRDIXZc/AYf3rtNQ6bE1B9XryH981qmSLwBUGYMtlkRl+2kkBVdLDQ6gwjixOi0yafJUkSG1auwOP1UnP69LzWz+EOYR8JTzp/DjA+wvZ8Da2tHK6twTwag1xnnLc1bgHqXj3DUOcoD310G0kJFsIB2Fy+hoG0LPxGE03/+wT7jx2btVHMIvAFQbgihvKNROwdhLvt4zdva/oCnB481/yQmZbGoowMjtadIhAMzlvdzi1n+I4btg21SHEWNJnZE7a32+28degwWelplFmLcJ5x4x+dn/p6nH6O/uk02atSyFufwbaxhVFys7J56O53o7/pVtLs7Zw6eZIn/vwCL+3azRmHY0Y9oGYU+JIkvU+SpDpJkhRJksrf8dxXJUlqliSpUZKkO2ZSjiAIV4+zi6Kcvcq/q9CCXiPxzKmJ88lsWLmCQDDIsVP181a3KrsXq1lDboJuwvZgfS36omUTrt57+gd4bd/bJCcmsn3zZjKXpYAKPfXzM13ywSfqUSIqFR8tGZsyeQPBUIhd+48gSRLJ77oHORLmPckWypeV0ud08tKu3Tz5woscr6/HP432/Zle4dcCDwB7zt8oSVIJ8BBQCmwHfiJJkubCwwVBuNZobVloMrMJVEfXZU0warg1N4ZXmt34Qud65iQnJrJk8WJONjbi8XrnvF6qqlJl91FuM00IdmXERaTrzIT2e6fLxUu7d2M2mbhrSyV6nY6U/Hi0Bs28zI9vrxmg7WAPK+7Nw5IW/TSybGk+mekp43Pr6JYUo8nIQj24h3XLl/PRe+9h28aNxJhM7D92nMefe543Dx6i3zn1+s4o8FVVrVdVdbIhYfcC/6uqakBV1TagGVg3k7IEQbh6GMorCNYcR/FHm1AeKInHE1R4vWViW/O6FctRVJWqeZg+uXUoiNMXYd07mnOCjdGy9WMjbEe9Xl58axeyLHP3LVswG6OjcTVamfSixPEBWHMlEoqw/zensKSZWf7uc72Bzi6Mcvh4HQPOYSRJwli5leDJo0ScA2g0GpbkLOb+bVt5/7u2szQ3h+YzZ3hqx6tTLnuu2vAzgc7zvu8a2yYIwnXAsLYCwiGCJ44AsDLdSG6CjmfqJ04zHB8bS2lBPvUtLQyPzO1SglUX639fXwuyBt2SYvzBIC++tYtAMMi7t1QSHzexT76txMqwfRTv8Ox2hzzfyZfacHV7qHikBK1+YsPH9i0VKIrKzr2HATDdvBUUZcLCKBD99LRl3Toevv8+Nq1ePeWyLxv4kiTtlCSpdpJ/9065lEuf/1OSJFVLklTd398/G6cUBGGO6UtWIJlM4+34kiRxX3E8dX0BGgcmhuWa0lI0Gg2HTs7txGpVdh+ZFi0ZcRPb70MNtWjzlhDRanl5926G3W7edfNmUpKSLjiHrdQKQPclpkueCXe/l+PPtZCzLo3sFSkXPJ+7yEZh3iJePbswSvZitPmF+HfvnPR8Br2eFUVLp1z+ZQNfVdWtqqoum+Tf85c4zA6cfzs8a2zbZOf/haqq5aqqlqekXPgDEATh6iPpdOhXriVw5OB4r5G7CuPQaySefcdVvtlkYmVRES0dnfQNzk2QhhWVI90+1tomNueokTChpnp0RaW89vZ+evoH2FpRQVZ6+qTnScqxoDdr56xZ58Bv66PdVj9y8b7+27dUUNvYQld3dH1bU+U2QqfrCTs6L3rMVM1Vk86fgYckSTJIkpQLLAEOz1FZgiAsAEN5BcpAH+H2FgDijRq25sWy47Qbb2jitAori4swGgwcPH5iTurSOBDAE1QuaM4Jt7WgBvyc1plot9vZvGYNBYsXXfQ8siyRUWKdk8A/c6SXjiN9rH6ggFir6aL7batcDzB+lW/cfCtI0kWv8q/ETLtl3i9JUhdQAbwkSdKrAKqq1gF/BE4BO4C/VlU1cvEzCYJwrRnvnll9YHzb/cUWPCGV15rdE/bV63SsKS2lq7eXzu7uWa/LYXu0F1D5OwdcNUQHXNVEJNaUllK29PLrw9pKk3D3+XD3z17PonAgwoHf1pOQGcuyd+Vcct/0FCurli1lx9mFUZJT0S9biW/3zhnPQjrTXjrPqqqapaqqQVXVNFVV7zjvue+qqpqvqupSVVVfmVEtBUG46miSktHmF4634wOsSDeSm6gfX/P2fMuWFBAXE8PB4ydmffrkKruPgiQ9SSbthO39hw/gNceQs3wl65ZfOMp2MuPt+LPYPfP48y2M9vvY+GgJsvbysbt9SwXtnd00tXYAYKzcGh3s1tI0o3qIkbaCIEybobyCUGMdyki03V6SJB4otlDfH6Ch3z9hX41Gw7rlZfQPDdHc0TFrdQiEFU72+C9ozmk+00Gk6RSB7Dwq15ZPecqEhMxYjBb9Jde5vRKubg8nX2yl4CYbthLrlI659aa1aLUadpxd73bjFtBq8c2wWUcEviAI02YorwBFIXD03C26OwvjMGikCYujnLVk8WKSEuI5fOIkkVmaPrmm108gok6YTqGrp4d9b7xOjMdN1qabkeWpR50kSdhKo+34M/0koqoq+39Th0anYd0Hi6Z8XIIlloo1Zby26yCKoiDHWTCs2YB/z07UyPRbx0XgC4IwbbolRUiWeAJHzjXrWAz/v717DY7zvA47/j97e3EHiBsJEBQvAAgC4J0LEitFJGVdSCuOHCnTRp4kjZ1k0g9Ox2k7k6nbmXb6IZ3ptNPph07bce14MhPZruNIncSxZImJbrZEEgQvxhI3gjeRIEiQBEHiutenH3ZBAthdYAHs6l0B5zeD0WLf9909WkEHL87zPOdx8kJ9Ee8MjDERnJvUHQ4H7Xv28HB8nJ7LlzMSQ8fgFE6BfTWxO/y7IyO8/dHHbBqP/dVhte5e8mvWtlYw+SDAw6GJFcV29dRtBrvu4/2njRSUJd8TN5XjR30M33/AuYuxMk7ekReJjtwj2L386a2a8JVSyyZOJ9aBdgKdp+fceb7aXMJkyPDzeYO3AJtra6mpquJMlz9hj9fl6BicpKXKosjjmLPx+F6PgMeDe2vjkl+ztjU2R38ls3WCU2FO/lUPFVtKaH4h9cygVJ49tI/8POtxq4W8tqeR/PwVbYyiCV8ptSJWmw8z9pBQf/fj53avz6O+3MP/S1LWERF8e/cyOT3Nhd6VbdY9HozSfTdA28YCJqem+LtZG48z0Iu7sRlxuxd9nfmKqwsoqsxb0cDtuTcHmBwJ8Mw3WnA4l55q8/Msjvj28w8fdxAKhZG8PKz2w0z/4n1MaHkdPTXhK6VWxNp3EBzOObN1Hg/e3gvQM2/wFmBDVSVb6zYuu+vjjHNDU0QM7F/v4u8/+JDJqSl+/egR1uXlEbrc/7h/zlKJxOfjd9/HRJdexx+5McrVOroAABVeSURBVIb/7Ws0PVdHdeO6ZcUAsbLOo/EJPj3bBcQWYZmJcQKdp5b1eprwlVIr4igqxt28c858fIAvNxZjuRLbJs84tGcPoXCYzovLb6zWMThJvtMwNNDJvdHR2MbjlZWEBvogHE65w1U6alsrCIyHGLmRWJZaiDGGT75/EU+Bi7bfTr/tQTKH9rVSVlL8uKzj2XMAR2nZshdhacJXSq2Y5fURvjpA5P6TfljFlpMXtxXx84ExxoOJM3LKS0tp2rqFrv5LjE0sb3C04+Ykx0uuc+vOHZ479GTj8WBv7I7Ys6N1Wa8LUNuyvDr+wC9ucbv3AW1fayKvxLPs9wdwuVy88OxBPjx5jsmpacTlIu/XvsT06V8QXUbLaU34SqkVs9p8AAl3+a+1lDIVTlx5O6Nt1y4EOP2rriW/5/3JEBWTA5SF79K+dw87tj1pNRzq8eOsrcNRuvxySmFFPqU1hUtK+IGJEKfe6KW6oYymI3XLfu/Zjh9tJxAI8sGnsc6keUdegGCQwMmPFrkykSZ8pdSKuZ7aiqNq/Zw6PsDOaovG8tjK22Rz2osLC9m5fTt9V69yf3R0Se954oyfFs8wGzfVs6/5STMyYwyhXj/uZdbvZ6tpKed27wjRSHprBjp/3E9gLMjTf9CCODKzN+6u5gZqqisf99Zx79iJs7pmWYuwNOErpVZMRLC87QQvdM6ZQRJrm1xC370A3XeTD84eaG3B43Zz6kL688t7Ll/h/s1ePotU8PLTc1fRRoYGiT4cxdOcXiuFhdS2VhCainDv6uK9/O9deUjPic9ofmkzlVtKV/zeMxwOBy8dOcSps34ePHwU3xjleYLnzxAZfbC018pYVEqpNc3y+jDTUwT9cztivtxYTJ4r+cpbgDzLYl9LM9cGBxkaXnxPjGuDg3xw+jT3KCVU2Yp73pTHULxh2koGbGfUpFnHN1HDL79/kbwSD95/svR5/4s5ftRHJBrlxMcdQGwRFtEI0798f0mvowlfKZUR1u4D4PEk1PGLLCcv1acevAXY3dREQV4en144v2A7g5mNx0tLynh7rJ62usKEc4I9fqSgENemLSv69wHIL7Eof6p40YTf9/4N7l5+yMHf2YGnYOnz/hfTsHUT9Zvrnux3u3kbri31S16EpQlfKZURkpeHZ9f+hDo+xAZvp8OGdy4lH7x1u1x4d+3k9t17XB+8lfScmY3HC/PzyX/qAGGcCQ3TgHj9vhVZQv+chdS2VnCn7wHhYPIeNtOPgnT83342NJfT8ExtRt4zmWNH27nQfYlbd2J/BeUdeZFQj5/w7eSfVzKa8JVSGWN524kM3SQ8OLcbZkuVRWOFhzd7Hqa8g2+ur6e0uJiTFy4QnddYbWxigr+Lbzz+lS89R+dwhPJ8J9vWzZ32GJ2cIHz9yrIXXCVT01JOJBRleCD5oPLpH/URnArzzDda0u7IuRzHjsb2H3j3w9iiq/zDzwMw/fE/pP0amvCVUhljeePTM+fd5cdW3pZy6X4w5eCt0+Hg0O7djDx8SP+1a4+fnw4E+OkHHxAMBvmN545SUljImVtTtG3MT0iwob5uMAZ3BgZsZ9Q0lyMCQ0nKOnf6H9D/wU12fnkL6+qKk1ydObXrq9jT0sg778c+W2f1Btwtu5n+IP2yjiZ8pVTGuDbU4ty0mUDnyYRjxxuLyXcJb3Y/THJlTP1Tm6gqL+f0r7oIRyKEwmF+9tFHPBwb5+Ujh6lct46royHuT0bmtEOeEer1g8OBe3vqPWOXylPgpnJbKbfm9dWJRqL88vsXKSzPY/9rDRl7v4UcO+rj8vWbDFyN7W+bf+QFwp9dTft6TfhKqYyyvD6C/vMJK0GLPA5eaijm3cvjjAeS18NFhPa9exifnKSrr5/34huPv/i0j43r1wOxdgpA0vp9sKcrtiagIHEwdyVqWysYvjxKaPpJd8/u9z5j5PoY7f+sGXeea4GrM+eFZ9twOhxPNkZ55jlwOtO+XhO+UiqjrLanIRwmeOFMwrFXm0uYDht+lmLwFmDThg3UbVjPp+fPc21wkMNeL/VPPWkv3DE4xcYSF7XFc2fDmEiEUF93RqZjzlfTWoGJGG73xea9Tz6YpvOv+6nbXcmWtvUZf79U1pWWcGj/Tt798FRsY5TSMqx9bWlfrwlfKZVRnuZdSEEhgY5PEo61VFk0VVq8lWLl7Qzf3r04nU68O3eyc/uTee3hqKHz1hTe2sRyTvjGNczkREYHbGds2L4Oh1MeT8889UYvkVAU3+9nd6A2mWNH2xkavkdXzwAApf/636d9rSZ8pVRGicuFZ18bgc6TCUl9pm3ywEgQ/3DqtshV5eX84W+9lrDxeN+9AOPBKAeTTcfsmVlwlbkB2xkuy0l1YxlDF+9z6+J9Ln8yxJ5X6imtyWzpKB1H2vdjWR7eibdacBSlP1isCV8plXGW10d05D7hK5cSjh1rWHzwFmKdIufrGJwCwJusft/rx1G2DueG7MyFr22t4N61R/ziu36Kq/LZ88q2xS/KgsKCfA4f2seJj08TXuKOYZrwlVIZZx2IzRmfv+oWoNDj4FhDMe9dGWcsxeBtKh2DkzSUeyjPT/xlEOqJNUzLVomltrUCDDy6M4nv6y24POkPlmba8ed8jD4a49S5pe0loAlfKZVxznXluBubk666BXitpYRA2PD2AoO38wXCUS7cnsZbm3h3Hxl9QGToJp4sDNjOqGoow1PgYrN3PU/tq87a+6TDt38XJUWFj2frpEsTvlIqKzzedkL93UQfJnZ0bK7Ko7nSStk2ORn/8DSBiEk9/x4y0hI5FafLwav/6Rme+5M9WXuPdLndLp5/to0PPz3L1HT6W0RqwldKZUWe1wfGEDh7OunxV1tKuDwS5Fd3Eve8Teb04BROgf1J7vBDvX5wuXA3rGxLwcUUVxfYWsqZ7dhRH1PTAT4+dS7tazThK6WywtXQhKOsPGkdH+Cl+mIK3KnbJs/XMThFS5VFkScxbQV7/LjrmxCPtaKYv0j2tW6numLdkso6mvCVUlkhDgfWgUMEOk9hIomzSQo9Do43FHPi8jiPFhm8HQ9G6R6expuknGNCIUKXerOy4CqXORwOXjrazidn0t8eUhO+UiprLK8PMzFOqDf5bJLXWkoJRAw/61948Pb80BQRk7ydQuhKP4SCWVlwleuOH/URiaQ/00kTvlIqazz72sDpTDlbp6nSoqVq8cHbjsFJLKewe31ewrHHC67WYMLfvu0ptm5Kf92BJnylVNY4CovwtOwm0Jm6zvxqcwlXHwS5cDv14G3H4BS7N+RhuRJTVqj3Is7qGpwVlRmJ+YtERPjWH72e9vma8JVSWWV5fYSvXSEyfCfp8ZcaiilcYPB2ZCrMpZEgbUlm5xhjCPZ0rbn6/WzPtKU/TVQTvlIqq6y2+KYoKe7yC9wOjjcWc+LKOA+nE+vRnbdi7RSSzb+P3h0mOnJvTSf8pdCEr5TKKmfdZpzVNSnr+BAbvA1GDH+fZPC2Y3CKQo+DHVWJUy6DvbEZKp6m1swFvIppwldKZZWIYLX5CFzoxASTrwrdXmHRWm3xVpI9bzsGpzhQk4/LkdgjJ9TjR6w8XFvrsxL7aqMJXymVdZbXB8EAwa7zKc95rbmUa6Mhzs8avB0aC3HzUShpd0yIdch0N7Ugzs9nx6kvOk34Sqms8+zaBx6LwJnETVFmvFhfRKHHMadt8kw75GTz76PTU4SvDKzJ6ZjLpQlfKZV1YllYew4Q6Pg05Xz7fLeDlxuL+cerE4zGB287Bicpz3dSv86TcH7oUi9EI1ntkLnaaMJXSn0uLG87kTtDRG5eT3nOq80l8cHb2EKsjltTtG3MT9rjPtQTG7B164Bt2laU8EXkv4hIr4j8SkTeEpGyWce+LSIDItInIsdWHqpS6ovM8j4NQODMyZTnNFZY7KqO7Xl7dTTE/clI0v73EBuwdW7ajKO4JCvxrkYrvcN/D9hpjNkN9APfBhCRFuB1oBU4DvxPEcmNnqJKKVs4q9fj2rwtZffMGa+2lHJ9NMT/OTMCJJ9/b6JRgn0X12T/nJVYUcI3xrxrjJlpg3cSqIs//irwI2NMwBhzFRgADq7kvZRSX3yW10fw4gWiE+Mpz3lxWxFFHgcnroxTW+xiY4k74ZzI4A3M2KOsbFi+mmWyhv8HwNvxxxuBG7OO3Yw/p5Raw6w2H0QiBM93pDwnLz54C8nv7iE2HRPQO/wlWjThi8gJEfEn+frqrHP+HRAG3lhqACLyxyJyRkTO3L17d6mXK6W+QNw7WpHCogVX3QL8Vmspbgcc3lyY9HiopwspLsG5cVM2wly1Fl2tYIx5YaHjIvJ14CvA8+bJfKtBYPZ/ibr4c8le/zvAdwC8Xm96m1sqpb6QxOnC2n8wtilKNIo4kt9zblvn4d3f35Z0dyuI3eF7mlpTXq+SW+ksnePAnwGvGGMmZx36W+B1EbFEZCvQCCTf2FIptaZYXh/R0RHCl/sXPC9Vso+OPSJy47o2TFuGlf56/B9AMfCeiJwXkf8NYIy5CPwY6AbeAb5pjEl/Wxal1KplHTgEIovO1kkl1NcNoAO2y7CiBhTGmIYFjv058OcreX2l1OrjKF2He3szgY5PKfraN5Z8fbDXDw4n7sYdWYhuddMCmFLqc2d5fYQu9RB5MLLka0M9Xbi2NeDIS74gS6WmCV8p9bmzvLFNUYJnTy3pOhMJE+rvwbND2ykshyZ8pdTnzlW/HUd5xaLTM+cLX7uCmZ7CvUPr98uhCV8p9bkTEawD7QTOncaEw4tfEPd4wZXO0FkWTfhKKVtYXh9mcoJgvOtlOkI9fhzllTiq1mcxstVLE75SyhaevW3gchFcwvTMUI8fd/POpO2S1eI04SulbOEoKMDTuiftOn7k/j0iw0PaP2cFNOErpWxjeX2Eb1wjfGdo0XND8fq9rrBdPk34SinbWG2x6ZnprLoN9vrB7cG9bXu2w1q1NOErpWzjrN2Es2ZjWmWdUI8fd+MOxJ3YH1+lRxO+Uso2IhLbFKXrLGZ6OuV5JhggdLlPp2OukCZ8pZStLK8PgkECXWdTnhMa6INwGLcO2K6IJnyllK08u/YiefkLlnUeD9hqwl8RTfhKKVuJ24NnzwECZz7lyR5KcwV7/Dhr6nCWrfuco1tdNOErpWxneX1E794h/Nm1hGPGGEK9fp2OmQGa8JVStrO87QAEznyScCxyZ4jo6ANdcJUBmvCVUrZzVlbj2tqQtI4fivfa0Tv8ldOEr5TKCZbXR6jHT3R8bM7zwR4/kl+Aa9MWewJbRTThK6VyguVth2iEwLnTc54P9fpxN7UiTqdNka0emvCVUjnB3dSKFJcQOHPy8XPRyQnC169oOSdDNOErpXKCOJ1Y+w8R7DyJiUYBCPX3QDSqA7YZoglfKZUzLK+P6MNRQpd6gPiArQjuphabI1sdNOErpXKGtf8gOByPZ+sEe/24Nm/FUVhkc2SrgyZ8pVTOcJSU4m5qIRAv64R6L+qG5RmkCV8plVMsr4/wQB/BC52YyQntkJlBmvCVUjnFansagPE3vgtow7RM0oSvlMopri31OCqrCfV14ygtw1mz0e6QVg1N+EqpnCIiWAdivXXcO3YiIjZHtHpowldK5ZyZZmq64CqzNOErpXKOdeAQBb/5OvlHj9kdyqrisjsApZSaT9weSv7wm3aHseroHb5SSq0RmvCVUmqN0ISvlFJrhCZ8pZRaIzThK6XUGqEJXyml1ghN+EoptUZowldKqTVCjDF2x/CYiIwBfXbHMU8lcM/uIJLIxbg0pvRoTOnLxbhyMaYmY0zxYifl2krbPmOM1+4gZhORM7kWE+RmXBpTejSm9OViXLkaUzrnaUlHKaXWCE34Sim1RuRawv+O3QEkkYsxQW7GpTGlR2NKXy7G9YWNKacGbZVSSmVPrt3hK6WUypKcSfgiclxE+kRkQET+TQ7E8xciMiwifrtjmSEim0TkfRHpFpGLIvKtHIgpT0ROi8iFeEz/0e6YZoiIU0TOichP7Y5lhohcE5EuETmf7syKbBORMhH5iYj0ikiPiPhsjqcp/vnMfD0SkT+1M6Z4XP8y/jPuF5EfikheDsT0rXg8F9P6jIwxtn8BTuAysA3wABeAFptjOgzsB/x2fz6zYqoB9scfFwP9OfA5CVAUf+wGTgHtdn9W8Xj+FfAD4Kd2xzIrpmtApd1xzIvpL4E/ij/2AGV2xzQrNidwG9hscxwbgatAfvz7HwNftzmmnYAfKCA2xf4E0LDQNblyh38QGDDGXDHGBIEfAV+1MyBjzEfAiJ0xzGeMGTLGnI0/HgN6iP0g2hmTMcaMx791x79sHxgSkTrg14Hv2h1LLhORUmI3N98DMMYEjTGj9kY1x/PAZWPMdbsDIZZU80XERSzJ3rI5nmbglDFm0hgTBj4EXlvoglxJ+BuBG7O+v4nNiSzXicgWYB+xO2pbxUsn54Fh4D1jjO0xAf8d+DMgancg8xjgXRHpFJE/tjsYYCtwF/h+vPz1XREptDuoWV4Hfmh3EMaYQeC/Ap8BQ8BDY8y79kaFH3hWRCpEpAB4Gdi00AW5kvDVEohIEfA3wJ8aYx7ZHY8xJmKM2QvUAQdFZKed8YjIV4BhY0ynnXGk8GvGmP3Al4Fvishhm+NxEStd/i9jzD5gArB9DA1ARDzAK8Bf50As64hVHbYCtUChiPyunTEZY3qA/wy8C7wDnAciC12TKwl/kLm/meriz6l5RMRNLNm/YYx50+54ZouXAt4HjtscyjPAKyJyjVh58Esi8lf2hhQTv1PEGDMMvEWsnGmnm8DNWX+V/YTYL4Bc8GXgrDHmjt2BAC8AV40xd40xIeBN4GmbY8IY8z1jzAFjzGHgAbFxvZRyJeF3AI0isjX+W/114G9tjinniIgQq7X2GGP+m93xAIhIlYiUxR/nAy8CvXbGZIz5tjGmzhizhdjP0j8aY2y9GwMQkUIRKZ55DLxE7M9y2xhjbgM3RKQp/tTzQLeNIc32NXKgnBP3GdAuIgXx/w+fJzaGZisRqY7/8yli9fsfLHR+TjRPM8aEReRPgJ8TG5X/C2PMRTtjEpEfAkeBShG5CfwHY8z37IyJ2J3r7wFd8Zo5wL81xvzMxphqgL8UESexG4gfG2NyZhpkjlkPvBXLF7iAHxhj3rE3JAD+BfBG/GbrCvANm+OZ+YX4IvDP7Y4FwBhzSkR+ApwFwsA5cmPF7d+ISAUQAr652IC7rrRVSqk1IldKOkoppbJME75SSq0RmvCVUmqN0ISvlFJrhCZ8pZRaIzThK6XUGqEJXyml1ghN+EoptUb8fzZI+kiqv2yEAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "flatui = [\"#9b59b6\", \"#3498db\", \"#95a5a6\", \"#e74c3c\", \"#34495e\", \"#2ecc71\"]\n",
    "for i in range(0, 5):\n",
    "  data = create_time_series_with_anomaly(\n",
    "      simple_data_gen=simple_data_gen,\n",
    "      num_seq=1,\n",
    "      seq_len=10,\n",
    "      pct_seq_before_anom=pct_seq_before_anom,\n",
    "      pct_seq_after_anom=pct_seq_after_anom,\n",
    "      norm_freq=test_norm_params[\"norm_freq\"],\n",
    "      norm_ampl=test_norm_params[\"norm_ampl\"],\n",
    "      norm_noise=test_norm_params[\"norm_noise\"])\n",
    "\n",
    "  sns.tsplot(data=data.reshape(-1), color=flatui[i%len(flatui)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create training and evaluation data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_train_norm_seq = 64000\n",
    "\n",
    "num_val_norm_1_seq = 6400\n",
    "num_val_norm_2_seq = 6400\n",
    "num_val_anom_seq = 6400\n",
    "\n",
    "num_test_norm_seq = 6400\n",
    "num_test_anom_seq = 6400\n",
    "\n",
    "seq_len = 30\n",
    "num_tags = 5\n",
    "tag_columns = [\"tag_{0}\".format(tag) for tag in range(0, num_tags)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tag_data_list = \n",
      "[{'norm_noise': 1.0, 'norm_ampl': 1.9281657901602325, 'norm_freq': 1.522478923631933}, {'norm_noise': 1.0, 'norm_ampl': 1.848539175940426, 'norm_freq': 1.3361444143088368}, {'norm_noise': 1.0, 'norm_ampl': 1.438327722951684, 'norm_freq': 1.4127534895254341}, {'norm_noise': 1.0, 'norm_ampl': 1.0729910167692451, 'norm_freq': 1.9380684122898564}, {'norm_noise': 1.0, 'norm_ampl': 1.8434709256315287, 'norm_freq': 1.1495975017794762}]\n"
     ]
    }
   ],
   "source": [
    "tag_data_list = [create_time_series_norm_params(\n",
    "    simple_data_gen=simple_data_gen)\n",
    "                 for tag in range(num_tags)]\n",
    "print(\"tag_data_list = \\n{}\".format(tag_data_list))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_norm_seq_array.shape = \n",
      "(64000, 5)\n"
     ]
    }
   ],
   "source": [
    "# Create training set using normal sequences\n",
    "train_norm_seq_list = [create_time_series_normal(\n",
    "    simple_data_gen=simple_data_gen,\n",
    "    num_seq=num_train_norm_seq,\n",
    "    seq_len=seq_len,\n",
    "    norm_freq=tag[\"norm_freq\"],\n",
    "    norm_ampl=tag[\"norm_ampl\"],\n",
    "    norm_noise=tag[\"norm_noise\"])\n",
    "                       for tag in tag_data_list]\n",
    "\n",
    "train_norm_seq_array = np.stack(\n",
    "  arrays=[np.stack(\n",
    "        arrays=[\n",
    "            np.array2string(\n",
    "                a=train_norm_seq_list[i][j], separator=\";\").replace(\"[\", \"\").replace(\"]\", \"\").replace(\" \", \"\").replace(\"\\n\", \"\")\n",
    "            for j in range(num_train_norm_seq)],\n",
    "        axis=0)\n",
    "          for i in range(num_tags)],\n",
    "    axis=1)\n",
    "\n",
    "np.random.shuffle(train_norm_seq_array)\n",
    "print(\"train_norm_seq_array.shape = \\n{}\".format(train_norm_seq_array.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "val_norm_1_seq_array.shape = \n",
      "(6400, 5)\n",
      "val_norm_2_seq_array.shape = \n",
      "(6400, 5)\n",
      "val_anom_seq_array.shape = \n",
      "(6400, 5)\n"
     ]
    }
   ],
   "source": [
    "# Create validation sets\n",
    "\n",
    "# Create set vn1 of normal sequences which will be used for early stopping\n",
    "# during training as well as using the error vectors to learn mu and sigma\n",
    "# for mahalanobis distance\n",
    "val_norm_1_seq_list = [create_time_series_normal(\n",
    "    simple_data_gen=simple_data_gen,\n",
    "    num_seq=num_val_norm_1_seq,\n",
    "    seq_len=seq_len,\n",
    "    norm_freq=tag[\"norm_freq\"],\n",
    "    norm_ampl=tag[\"norm_ampl\"],\n",
    "    norm_noise=tag[\"norm_noise\"])\n",
    "                       for tag in tag_data_list]\n",
    "\n",
    "val_norm_1_seq_array = np.stack(\n",
    "    arrays=[np.stack(\n",
    "        arrays=[\n",
    "            np.array2string(\n",
    "                a=val_norm_1_seq_list[i][j], separator=\";\").replace(\"[\", \"\").replace(\"]\", \"\").replace(\" \", \"\").replace(\"\\n\", \"\")\n",
    "            for j in range(num_val_norm_1_seq)],\n",
    "        axis=0)\n",
    "            for i in range(num_tags)],\n",
    "    axis=1)\n",
    "np.random.shuffle(val_norm_1_seq_array)\n",
    "print(\"val_norm_1_seq_array.shape = \\n{}\".format(val_norm_1_seq_array.shape))\n",
    "\n",
    "# Create set vn2 of normal sequences which will be used for tuning the\n",
    "# anomaly thresholds\n",
    "val_norm_2_seq_list = [create_time_series_normal(\n",
    "    simple_data_gen=simple_data_gen,\n",
    "    num_seq=num_val_norm_2_seq,\n",
    "    seq_len=seq_len,\n",
    "    norm_freq=tag[\"norm_freq\"],\n",
    "    norm_ampl=tag[\"norm_ampl\"],\n",
    "    norm_noise=tag[\"norm_noise\"])\n",
    "                       for tag in tag_data_list]\n",
    "\n",
    "val_norm_2_seq_array = np.stack(\n",
    "    arrays=[np.stack(\n",
    "        arrays=[\n",
    "            np.array2string(\n",
    "                a=val_norm_2_seq_list[i][j], separator=\";\").replace(\"[\", \"\").replace(\"]\", \"\").replace(\" \", \"\").replace(\"\\n\", \"\")\n",
    "            for j in range(num_val_norm_2_seq)],\n",
    "        axis=0)\n",
    "            for i in range(num_tags)],\n",
    "    axis=1)\n",
    "np.random.shuffle(val_norm_2_seq_array)\n",
    "print(\"val_norm_2_seq_array.shape = \\n{}\".format(val_norm_2_seq_array.shape))\n",
    "\n",
    "# Create set va of anomalous sequences which will be used for tuning the\n",
    "# anomaly thresholds\n",
    "val_anom_seq_list = [create_time_series_with_anomaly(\n",
    "    simple_data_gen=simple_data_gen,\n",
    "    num_seq=num_val_anom_seq,\n",
    "    seq_len=seq_len,\n",
    "    pct_seq_before_anom=pct_seq_before_anom,\n",
    "    pct_seq_after_anom=pct_seq_after_anom,\n",
    "    norm_freq=tag[\"norm_freq\"],\n",
    "    norm_ampl=tag[\"norm_ampl\"],\n",
    "    norm_noise=tag[\"norm_noise\"])\n",
    "                       for tag in tag_data_list]\n",
    "\n",
    "val_anom_seq_array = np.stack(\n",
    "    arrays=[np.stack(\n",
    "        arrays=[\n",
    "            np.array2string(\n",
    "                a=val_anom_seq_list[i][j], separator=\";\").replace(\"[\", \"\").replace(\"]\", \"\").replace(\" \", \"\").replace(\"\\n\", \"\")\n",
    "            for j in range(num_val_anom_seq)],\n",
    "        axis=0)\n",
    "            for i in range(num_tags)],\n",
    "    axis=1)\n",
    "np.random.shuffle(val_anom_seq_array)\n",
    "print(\"val_anom_seq_array.shape = \\n{}\".format(val_anom_seq_array.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test_norm_seq_array.shape = \n",
      "(6400, 5)\n",
      "test_anom_seq_array.shape = \n",
      "(6400, 5)\n"
     ]
    }
   ],
   "source": [
    "# Create test sets\n",
    "\n",
    "# Create set tn of normal sequences which will be used for testing model\n",
    "test_norm_seq_list = [create_time_series_normal(\n",
    "    simple_data_gen=simple_data_gen,\n",
    "    num_seq=num_test_norm_seq,\n",
    "    seq_len=seq_len,\n",
    "    norm_freq=tag[\"norm_freq\"],\n",
    "    norm_ampl=tag[\"norm_ampl\"],\n",
    "    norm_noise=tag[\"norm_noise\"])\n",
    "                       for tag in tag_data_list]\n",
    "\n",
    "test_norm_seq_array = np.stack(\n",
    "    arrays=[np.stack(\n",
    "        arrays=[\n",
    "            np.array2string(\n",
    "                a=test_norm_seq_list[i][j], separator=\";\").replace(\"[\", \"\").replace(\"]\", \"\").replace(\" \", \"\").replace(\"\\n\", \"\")\n",
    "            for j in range(num_test_norm_seq)],\n",
    "        axis=0)\n",
    "            for i in range(num_tags)],\n",
    "    axis=1)\n",
    "np.random.shuffle(test_norm_seq_array)\n",
    "print(\"test_norm_seq_array.shape = \\n{}\".format(test_norm_seq_array.shape))\n",
    "\n",
    "# Create set ta of anomalous sequences which will be used for testing model\n",
    "test_anom_seq_list = [create_time_series_with_anomaly(\n",
    "    simple_data_gen=simple_data_gen,\n",
    "    num_seq=num_test_anom_seq,\n",
    "    seq_len=seq_len,\n",
    "    pct_seq_before_anom=pct_seq_before_anom,\n",
    "    pct_seq_after_anom=pct_seq_after_anom,\n",
    "    norm_freq=tag[\"norm_freq\"],\n",
    "    norm_ampl=tag[\"norm_ampl\"],\n",
    "    norm_noise=tag[\"norm_noise\"])\n",
    "                      for tag in tag_data_list]\n",
    "\n",
    "test_anom_seq_array = np.stack(\n",
    "    arrays=[np.stack(\n",
    "        arrays=[\n",
    "            np.array2string(\n",
    "                a=test_anom_seq_list[i][j], separator=\";\").replace(\"[\", \"\").replace(\"]\", \"\").replace(\" \", \"\").replace(\"\\n\", \"\")\n",
    "            for j in range(num_test_anom_seq)],\n",
    "        axis=0)\n",
    "            for i in range(num_tags)],\n",
    "    axis=1)\n",
    "np.random.shuffle(test_anom_seq_array)\n",
    "print(\"test_anom_seq_array.shape = \\n{}\".format(test_anom_seq_array.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "labeled_val_mixed_seq_array.shape = \n",
      "(12800, 6)\n",
      "unlabeled_val_mixed_seq_array.shape = \n",
      "(12800, 5)\n",
      "labeled_test_mixed_seq_array.shape = \n",
      "(12800, 6)\n",
      "unlabeled_test_mixed_seq_array.shape = \n",
      "(12800, 5)\n"
     ]
    }
   ],
   "source": [
    "# Combine vn2 and va sets for tuning anomaly thresholds\n",
    "# Labeled\n",
    "labeled_val_norm_2_seq_array = np.concatenate(\n",
    "    seq=[val_norm_2_seq_array,\n",
    "         np.zeros(shape=[val_norm_2_seq_array.shape[0], 1],\n",
    "                  dtype=np.int64)],\n",
    "    axis=1)\n",
    "\n",
    "labeled_val_anom_seq_array = np.concatenate(\n",
    "    seq=[val_anom_seq_array,\n",
    "         np.ones(shape=[val_anom_seq_array.shape[0], 1],\n",
    "                 dtype=np.int64)],\n",
    "    axis=1)\n",
    "\n",
    "labeled_val_mixed_seq_array = np.concatenate(\n",
    "    seq=[labeled_val_norm_2_seq_array,\n",
    "         labeled_val_anom_seq_array],\n",
    "    axis=0)\n",
    "\n",
    "np.random.shuffle(labeled_val_mixed_seq_array)\n",
    "print(\"labeled_val_mixed_seq_array.shape = \\n{}\".format(\n",
    "    labeled_val_mixed_seq_array.shape))\n",
    "\n",
    "# Unlabeled\n",
    "unlabeled_val_mixed_seq_array = np.concatenate(\n",
    "    seq=[val_norm_2_seq_array,\n",
    "         val_anom_seq_array],\n",
    "    axis=0)\n",
    "\n",
    "np.random.shuffle(unlabeled_val_mixed_seq_array)\n",
    "print(\"unlabeled_val_mixed_seq_array.shape = \\n{}\".format(\n",
    "    unlabeled_val_mixed_seq_array.shape))\n",
    "\n",
    "# Combine tn and ta sets for testing model\n",
    "# Labeled\n",
    "labeled_test_norm_seq_array = np.concatenate(\n",
    "    seq=[test_norm_seq_array,\n",
    "         np.zeros(shape=[test_norm_seq_array.shape[0], 1],\n",
    "                  dtype=np.int64)],\n",
    "    axis=1)\n",
    "\n",
    "labeled_test_anom_seq_array = np.concatenate(\n",
    "    seq=[test_anom_seq_array,\n",
    "         np.ones(shape=[test_anom_seq_array.shape[0], 1],\n",
    "                 dtype=np.int64)],\n",
    "    axis=1)\n",
    "\n",
    "labeled_test_mixed_seq_array = np.concatenate(\n",
    "    seq=[labeled_test_norm_seq_array,\n",
    "         labeled_test_anom_seq_array],\n",
    "    axis=0)\n",
    "\n",
    "np.random.shuffle(labeled_test_mixed_seq_array)\n",
    "print(\"labeled_test_mixed_seq_array.shape = \\n{}\".format(\n",
    "    labeled_test_mixed_seq_array.shape))\n",
    "\n",
    "# Unlabeled\n",
    "unlabeled_test_mixed_seq_array = np.concatenate(\n",
    "    seq=[test_norm_seq_array,\n",
    "         test_anom_seq_array],\n",
    "    axis=0)\n",
    "\n",
    "np.random.shuffle(unlabeled_test_mixed_seq_array)\n",
    "print(\"unlabeled_test_mixed_seq_array.shape = \\n{}\".format(\n",
    "    unlabeled_test_mixed_seq_array.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Now save out the errays to csv files\n",
    "np.savetxt(\n",
    "    fname=\"data/train_norm_seq.csv\",\n",
    "    X=train_norm_seq_array,\n",
    "    fmt=\"%s\",\n",
    "    delimiter=\",\")\n",
    "\n",
    "np.savetxt(\n",
    "    fname=\"data/val_norm_1_seq.csv\",\n",
    "    X=val_norm_1_seq_array,\n",
    "    fmt=\"%s\",\n",
    "    delimiter=\",\")\n",
    "\n",
    "np.savetxt(\n",
    "    fname=\"data/labeled_val_mixed_seq.csv\",\n",
    "    X=labeled_val_mixed_seq_array,\n",
    "    fmt=\"%s\",\n",
    "    delimiter=\",\")\n",
    "\n",
    "np.savetxt(\n",
    "    fname=\"data/unlabeled_val_mixed_seq.csv\",\n",
    "    X=unlabeled_val_mixed_seq_array,\n",
    "    fmt=\"%s\",\n",
    "    delimiter=\",\")\n",
    "\n",
    "np.savetxt(\n",
    "    fname=\"data/labeled_test_mixed_seq.csv\",\n",
    "    X=labeled_test_mixed_seq_array,\n",
    "    fmt=\"%s\",\n",
    "    delimiter=\",\")\n",
    "\n",
    "np.savetxt(\n",
    "    fname=\"data/unlabeled_test_mixed_seq.csv\",\n",
    "    X=unlabeled_test_mixed_seq_array,\n",
    "    fmt=\"%s\",\n",
    "    delimiter=\",\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.3022643;1.97741574;0.38066236;-1.50524036;0.03398575;2.86387408;0.82932733;-1.66266919;-0.30088448;1.83663827;1.70331202;-0.7977396;-0.07725102;1.62403069;1.68445044;-0.72749672;-0.54088439;1.53208518;1.70428402;-0.43001789;-0.78420005;1.80720923;2.33382062;0.09766335;-1.08957973;1.49531316;2.44820779;0.37995274;-1.56186047;0.4624977,0.80775108;1.92516898;1.04338343;-1.18606867;-1.29619049;0.83471436;2.18648646;0.36174395;-1.17189871;-0.34432044;1.78657379;1.60821403;-0.23485116;-0.84391421;0.40830408;1.84592228;1.24189034;-0.63605423;-1.18576172;1.22239184;2.5515058;1.31717207;-0.90679716;-0.57889382;1.89375664;2.07447679;0.17685387;-1.34475346;-0.30079106;2.05690318,0.10360912;1.75866143;0.64030032;-0.95984526;-0.03331013;1.49000045;1.21406383;-0.38671276;-1.03908207;0.64524375;2.04170946;0.26432184;-0.58799729;0.21737775;1.67390694;1.68405018;-0.03228011;-1.15875351;0.47655221;1.77170688;0.04403952;-0.92184037;0.25679826;1.66788979;1.71996546;-0.08309814;-1.00446831;1.25783949;1.98540767;0.27677776,0.93019733;1.77987304;0.24664646;0.24548586;2.03624256;-0.17245952;0.08722863;1.30763685;0.49197643;-0.33164112;1.30882526;0.69495278;-0.98085167;0.22972178;1.14579894;-0.53011148;0.42812591;1.36067144;-0.05230748;-0.10494896;1.85762463;0.9848918;-0.70136687;0.76685747;1.00140502;-0.09602755;0.14551639;1.90404314;-0.56285564;-0.15476204,0.87417144;2.01045426;2.21576653;-0.53353454;-1.41938062;-0.10789336;1.43385673;2.5067007;1.12278943;-1.16169379;-0.65065985;0.42137292;2.09713966;1.65891235;-0.24909393;-1.61185973;-0.31238508;1.43183524;2.56452412;0.58891041;-0.96208474;-1.04683012;1.07856;2.07471828;1.64104258;-0.19679446;-1.68932926;0.18419557;2.01193127;2.41460266\n",
      "0.24596135;2.10708535;1.02145829;-1.12034337;0.14875274;2.34068904;1.26221978;-1.4681539;0.21067637;2.02284072;1.46377653;-1.45633489;-0.97341206;1.95835624;1.93228952;-1.44183852;-1.20033017;1.41267979;2.3411136;-0.91631218;-0.6794843;1.12919236;2.19724064;-0.61704252;-1.30336524;1.2187896;2.34847542;-0.47616853;-0.8867459;0.73103798,0.40581846;2.00452416;1.5905424;-1.22840304;-0.70378544;1.611961;2.80184652;0.92628496;-0.88587843;-0.72331407;1.43826372;1.67318215;-0.11901583;-0.85092155;0.30345293;1.90384895;1.87005624;-0.35094254;-0.7085078;0.75122249;2.54231953;0.85865286;-1.23152051;-0.17252396;1.91556861;2.29825605;0.30754989;-1.26032277;0.01891577;1.67402596,0.20540886;2.07981869;0.64922754;-0.54778854;-0.02670144;1.88029463;1.70719293;-0.18214426;-0.53974186;0.74157781;1.68974188;1.22244467;-0.86390941;-0.53056544;1.92953766;1.52098398;-0.25112352;-1.10352053;0.72769303;2.30247274;0.31320637;-0.4885826;-0.09722127;1.54025675;1.78760976;-0.12897365;-1.14298693;0.90831263;1.55558787;0.36389237,0.95811322;1.40319116;-0.14156181;0.31993878;1.29677391;-0.25335795;-0.46998063;1.16918116;0.55113391;-0.49684;1.510013;1.36895675;-0.19915966;0.2097781;1.29674615;-0.20530869;-0.0106477;1.2373702;0.41811892;-0.01770256;1.15395187;0.74771337;-0.67511755;1.59157539;1.54237391;-0.3421872;0.6259201;1.12373312;-0.42414679;0.26253467,0.5311787;2.06734037;1.64238399;0.02514724;-1.09762318;-0.54067841;1.44956124;1.83601608;0.93657681;-1.08529651;-0.93095762;0.18143537;2.33916157;2.15378958;-0.23850392;-1.21758279;-0.45067291;1.72451259;2.28876558;1.17448928;-1.40217519;-1.28940298;1.08231377;2.22670697;1.44688261;-0.51701906;-1.69412289;-0.64974925;1.54351237;2.38088107\n",
      "0.37481653;2.76008171;1.16730227;-1.15735038;-0.3686691;2.60262556;1.18037573;-1.48810357;-0.61452309;2.57009342;1.72084829;-1.29164658;-0.7003692;2.09449548;1.50338063;-0.74461169;-0.96781996;2.07733818;1.49450297;-0.67330308;-0.81073613;1.42490405;2.57721613;-0.46013253;-1.21252996;0.7493979;2.77537526;-0.48346771;-1.83748606;0.33637922,0.94051685;2.00052978;1.80454839;-0.81757897;-1.44982032;1.54362824;2.5481438;0.82302453;-1.28858059;-0.20063435;1.78275516;2.46421023;-0.22269225;-1.66440633;0.23478773;2.48053567;2.02702909;-0.91033683;-0.80711206;1.40605452;2.71631156;0.63769083;-1.28292427;-0.9369005;1.81542608;1.7283683;0.264608;-1.3251993;0.0284354;1.85759841,0.0297658;1.97708676;0.99767995;-0.38186138;-0.17474294;1.26488638;1.58989017;-0.31116138;-0.93608661;0.31808183;1.69224866;0.32916451;-0.4816593;-0.39723712;1.650029;1.19069608;0.03934714;-0.93507165;1.10487414;2.24018342;0.74285311;-0.76159293;0.07894132;1.63556754;1.52117956;-0.46728161;-0.70490742;1.17925361;1.83387792;-0.06553921,0.62483115;1.43988842;-0.4027;0.01069014;2.04515407;-0.08622456;-0.38089125;0.97135457;0.97120583;-0.86796225;1.24499461;1.41520466;-0.80854542;0.4049237;1.61119084;0.20855627;0.50438913;1.82677994;0.5679521;-0.51830935;1.43103668;0.28581343;-0.0539204;0.77488824;1.3752566;-0.35779914;0.51703167;1.66035936;-0.72691297;0.04592184,0.86988424;2.42825345;2.30224109;-0.27709376;-1.14270808;-0.21163049;2.05302123;2.07654667;1.00195644;-1.13019753;-1.29283486;0.53215152;1.77557338;1.38633172;-0.28873701;-1.60964955;-0.49173281;1.39653135;2.7232982;0.81039076;-0.77135939;-1.20155071;1.02442186;2.530715;1.77457505;-0.1393758;-1.01455034;0.27934101;1.73966905;1.9961735\n"
     ]
    }
   ],
   "source": [
    "!head -3 data/train_norm_seq.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.06291295;2.71548288;0.87626983;-1.19140039;0.25018395;2.03753108;0.62163365;-1.09652187;0.17463795;1.75435652;0.94195048;-0.70546983;-0.95329836;1.66985154;1.87606722;-1.22469154;-0.42922341;2.07042872;1.58834255;-0.83304065;-0.92249714;1.76867717;1.7626847;-0.73332969;-0.97852344;1.25718525;2.45930821;0.13662622;-1.00310473;1.20912671,0.34541279;2.07792462;1.57798741;-0.77999459;-1.16303979;1.63228064;2.40564196;1.06140524;-1.39961115;-0.63789475;2.07121551;2.14897527;-0.47085746;-1.36826862;0.53846211;2.39344195;1.3673265;-0.51734956;-0.901496;0.95496;2.14108996;1.23467064;-0.72785641;-0.24851427;2.01725256;2.68001093;-0.21223795;-1.33466688;-0.30973441;2.12115207,0.25437633;1.73807147;0.46440636;-0.69544318;-0.55893622;1.153598;1.89168914;0.19287571;-1.27936749;0.82890319;1.64913854;1.22775809;-1.3305177;-0.03158416;1.53196291;1.63888235;-0.48198591;-0.87786421;0.65696015;1.71282196;0.2951191;-0.92983897;-0.40499592;1.49448942;0.87909154;-0.85818273;-1.15455786;0.67928905;1.95903473;0.4716482,0.69538454;1.36959121;-0.62492219;0.16893606;1.38233811;-0.02778341;-0.16361751;1.71335447;0.28006373;-0.19817489;0.96632677;0.85625693;-0.24336411;0.45773435;1.86006568;-0.03800424;-0.22327837;1.96810575;0.35921329;-0.44833874;1.79354742;1.14263531;-0.45305352;1.5160312;1.04537016;-0.8714453;0.31642479;1.93553355;0.10426176;-0.21548219,0.74556019;2.60766976;1.87523021;-0.2900504;-1.62924736;-0.46036951;1.81020618;2.57246418;0.66975205;-1.22003476;-1.47858141;1.06767812;2.05259098;1.81069055;-0.43912845;-1.37825305;-0.18681281;1.64186768;2.58362482;0.71249438;-0.7323822;-0.95044254;0.87727526;2.36319036;1.84136892;-0.17245208;-1.65672628;0.08520005;1.4396179;2.61486055\n",
      "0.15878735;2.53260048;0.19225577;-1.69586262;-0.2495846;2.07963762;1.3904624;-1.31699748;0.13672988;1.9073475;1.08076887;-0.90947037;-0.25851572;2.32804288;1.87417618;-0.91332952;-1.30408897;1.64905484;2.24533573;-0.48507923;-1.15050841;1.28455569;2.66586515;-0.11357603;-1.06613174;1.43393339;2.23543945;0.44129715;-1.37760707;1.3193372,0.31529042;2.7739068;1.78999322;-0.99413243;-1.11519441;1.62855128;2.27376505;0.45495236;-0.99848834;-0.93350328;2.02905903;1.68412739;0.04976223;-1.79120855;0.67813509;2.48463916;1.80918867;-0.54067767;-0.77966374;0.68062891;2.75842196;0.54255705;-0.91642163;-0.40513405;2.11215982;1.98797867;0.48242998;-1.71513384;-0.10722785;2.03671422,0.18548074;2.18164684;0.4840668;-0.82798093;-0.73707022;1.01771742;1.51005566;0.09216597;-1.17410058;0.42600852;2.3697375;0.78242763;-0.55134371;-0.66894539;1.20286428;1.06972449;-0.19211959;-1.16871874;0.72702701;1.52732805;0.7554756;-0.86836392;-0.08847553;1.310246;0.91203083;-0.94422946;-0.93945835;0.8463206;1.90632278;0.4686935,0.64177924;1.96626984;-0.27823262;0.34049532;1.36083471;0.14684958;-0.38760972;1.83590763;0.33882396;-0.11496553;1.53625489;1.25199619;-0.0688336;0.94253254;1.80802037;0.1994377;0.06335024;1.48038328;0.38724997;-0.54157984;1.59523502;0.81274661;-0.69427549;0.73134089;0.86920916;-0.23881847;1.0794182;1.90086312;-0.66016001;0.47119656,0.71733637;2.14012882;1.76751185;0.24938686;-0.94917587;-0.92007719;1.06980278;1.91994276;1.12029144;-0.71458806;-0.71193607;0.440705;2.38721982;1.71043896;-0.36272984;-1.52830878;0.08523256;1.39616691;2.28531049;0.34516088;-0.60730774;-1.04185974;0.69829545;2.73498339;1.97255534;-0.39685364;-1.78469594;-0.40892963;1.33903903;1.73712731\n",
      "0.27690979;2.17901672;1.03293837;-1.52369535;-0.09387592;2.6888899;1.29563762;-1.29206043;0.15753064;2.08457725;1.28792931;-1.59430423;-0.31718165;2.13987267;1.67958695;-0.48956143;-1.07689501;2.26479975;2.24504794;-0.97706386;-0.74974335;1.91322032;1.91419422;-0.30932278;-1.19113486;0.72258718;2.57428259;0.13353251;-1.48158493;1.3011219,0.73334638;2.5317775;1.4690973;-0.78862637;-0.77004499;1.51770209;2.33860594;0.50770615;-1.14673198;-0.78647323;2.11645188;2.49020734;-0.42771791;-0.95173912;0.40958621;1.83463809;1.94120666;-0.5314409;-1.29125342;1.25667928;1.88493205;0.7341094;-1.01815458;-1.0529716;2.04635533;2.46564276;-0.12710657;-1.19726159;0.17752564;1.90662961,0.73828491;2.08206595;1.41700524;-0.78163265;-0.29930357;1.67480112;1.48981001;-0.46005494;-0.95498892;0.3688836;2.13539594;0.82023582;-1.07179218;0.17076831;1.21389921;1.82524259;-0.54131485;-0.70761693;1.05220712;2.16218229;0.77493807;-1.36025335;-0.06991684;1.94246831;1.02346592;-0.80638242;-0.74386334;0.843634;2.01013749;0.70422073,0.49948191;1.75492266;0.07430166;0.21342187;1.65689739;0.16396412;-0.34325491;1.03947004;0.9337376;-0.40073592;0.81217858;1.25398349;-0.85749746;0.56242852;0.98255593;-0.40833143;-0.41617578;1.35492643;0.25931931;-0.43853056;1.32237117;1.12987208;-0.68089791;0.6844397;0.91189999;-0.98946167;0.49842573;1.42072361;-0.38313958;-0.33968631,0.57711391;2.35122703;2.31725778;0.3126593;-1.10222397;-0.00392846;1.66800123;2.04122029;1.39437039;-0.84634591;-1.43798286;0.2014949;2.3106025;2.18892213;0.1688154;-1.46109447;-0.1061763;1.62679294;2.5455641;1.0649417;-0.95207551;-1.14545348;0.45419293;2.65470915;1.36815211;-0.38649692;-1.12890782;-0.0518857;2.15371035;2.27903083\n"
     ]
    }
   ],
   "source": [
    "!head -3 data/val_norm_1_seq.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.36538387;2.87904188;0.78538932;-1.48863723;0.22470137;2.25999073;0.73740157;-1.55615568;-0.35997603;2.59820426;1.66992924;-0.68580065;-0.95250251;1.63113332;1.70053223;-1.29359908;-0.68974819;1.95711898;1.65158459;-0.3959467;-1.00743804;-28.21307963;-22.54614801;-1.72136902;-22.10617028;-11.97295264;38.87270208;-5.89986972;21.21338902;-4.1166863,0.35761259;2.06898201;1.43395081;-0.41504438;-1.01690525;1.55265669;2.69510891;0.23422726;-1.67723693;-0.60654454;1.48683565;1.70861067;-0.06142698;-1.00121184;-0.14752923;2.61871821;1.66327897;-0.86008645;-0.89531639;0.81957888;2.60597264;13.6722319;-11.71524178;10.13719366;12.37154846;31.31654387;2.69046333;13.42415719;3.43490216;39.71377467,0.5313422;2.0767803;1.39540675;-0.5235833;-0.72081401;1.91271945;1.88011061;-0.14954812;-1.28838928;1.11629057;1.99222528;1.15773105;-0.70212163;0.06286698;1.95947011;1.15559086;0.0407887;-0.33580604;1.02871965;1.66618503;0.56320882;-16.91356527;0.85782939;-29.30288328;-13.59202806;-14.19718567;10.0068843;-19.8694444;-17.10683318;4.88359244,0.23128166;1.55584641;-0.03931243;0.10695024;1.18360686;-0.05276037;-0.11196006;1.50870319;0.7776962;-0.42930586;1.09631002;1.15540031;-0.9237052;0.40979658;1.07620796;-0.74615062;0.21524273;1.77414464;-0.12696763;0.10944308;1.85847654;6.4647466;3.90167461;-22.52425845;19.67166216;-3.74962358;4.02696374;-17.35489771;-2.05293005;-2.25498675,0.65366725;2.26288082;2.06887742;0.18301176;-1.55937137;-0.34692774;1.91177552;2.62414341;0.69200593;-0.52897904;-1.03382145;0.58655048;2.72396193;2.19482108;0.1221414;-1.49176473;-0.19064058;1.93779966;1.84359866;0.7305797;-0.7784384;14.45973821;-20.12545261;-35.04703657;18.96843904;-5.92157404;27.44478667;3.98278874;11.72221469;-40.65757598,1\n",
      "0.70641257;2.3627268;0.83419539;-1.0473414;-0.3545738;2.11080169;1.52019449;-1.56925919;-0.23922892;2.35115056;1.56957562;-0.77843184;-0.10401627;2.47634886;1.69463249;-1.19912771;-0.50231184;2.21602279;2.11127351;-1.02883157;-1.47819937;1.52128313;1.7665291;-0.57184707;-1.45461553;1.48855712;1.87081681;0.01054461;-1.26331165;0.96534884,0.13312314;2.25151277;1.67340413;-0.46727852;-0.68552731;1.32575363;2.69670979;1.01868328;-1.31549979;-0.12296289;1.83501546;2.47627586;0.2227062;-1.74632446;0.69736134;1.89663846;1.40220055;-0.5190897;-1.55033594;0.48278653;2.48546714;0.41980231;-0.84316859;-0.76696619;1.4126478;2.44404436;0.53033327;-1.80873275;0.23449072;2.2215574,0.35051009;1.4652541;1.25483996;-0.58877561;-0.1533796;1.41998165;2.12359547;-0.16969472;-0.40473561;0.80994894;1.45755896;0.38714524;-0.94862613;-0.31738058;1.1617508;1.29652565;-0.04653193;-0.72470768;1.05676746;1.97372374;0.70439479;-0.72078077;0.34159553;1.79577215;1.29610102;-0.46618223;-0.90300424;0.67691584;2.28036132;0.67422009,0.16733551;1.17353535;-0.3607475;0.03396302;1.42157649;-0.1019682;-0.36816249;1.81620997;0.58398513;-0.65776385;0.61402257;0.68809619;-0.56692999;0.26136634;1.60616447;-0.7194838;-0.11717542;1.10084582;0.28218014;-0.13291253;1.42020928;1.0043428;-0.62936495;1.18059517;1.44622357;-0.0500018;0.2475019;1.0462193;-0.30267857;-0.25396021,5.26520611e-01;2.30241661e+00;2.27063321e+00;3.05974369e-01;-8.94874838e-01;-7.11130008e-01;1.42714190e+00;2.15222509e+00;9.83913907e-01;-9.31224795e-01;-6.72388756e-01;3.42151921e-01;2.10421322e+00;1.86021464e+00;-5.55176502e-03;-1.11483837e+00;8.52936452e-02;1.74830829e+00;2.70410114e+00;7.01396578e-01;-5.80429897e-01;-1.27313294e+00;9.53774157e-01;2.76340232e+00;1.41046207e+00;-1.41932306e-01;-1.43341380e+00;-2.33970927e-03;1.88464296e+00;2.29193789e+00,0\n",
      "0.70324491;2.45675959;0.75254358;-0.92046207;0.40789402;2.85735742;1.24818367;-1.00413185;-0.47707187;1.85316645;0.94152278;-0.940727;-0.27812073;1.71165939;1.65178816;-0.45010111;-0.68820874;1.45434223;2.43510086;-0.36741197;-0.8925696;1.7016844;2.23478018;-0.10727159;-1.0168944;0.93692606;2.23794377;0.0300178;-1.50350199;0.38933741,0.91930645;2.56427931;0.98339951;-1.08720942;-1.07957387;1.06252863;1.87694058;0.61319861;-1.73198278;-0.80445594;2.18909151;1.70478749;-0.38203161;-1.60897643;-0.0497257;2.61853068;1.49390843;-0.78553309;-1.49719025;0.62709689;2.30971471;0.46760736;-1.20735981;-0.99943649;2.0748549;2.10308734;0.07311102;-1.76108535;-0.07538355;1.92520501,8.28140352e-01;1.73898346e+00;1.40945955e+00;-5.12991801e-01;-1.91472075e-01;1.66979551e+00;1.88220658e+00;-2.75742929e-01;-7.32001504e-01;3.96379301e-01;1.45973736e+00;8.02312649e-01;-1.01557050e+00;-4.15290263e-01;2.14817092e+00;1.93628725e+00;-3.02529926e-01;-6.11200445e-01;1.05978964e+00;1.71177586e+00;7.57148849e-01;-1.04410034e+00;4.86994855e-01;1.70948164e+00;8.72429954e-01;-3.30411803e-01;-8.19595761e-01;1.55240549e+00;1.91154659e+00;2.03264459e-03,0.87662948;1.94036716;-0.10441909;-0.12113882;1.37817741;0.26141908;-0.0065548;1.25858213;0.63592566;-0.96679381;1.29654358;1.1379269;-0.21024997;0.96338224;1.07662901;-0.4443461;0.08808021;1.27410379;0.27029093;-0.71886144;1.46660103;0.31646489;-0.32887336;0.75966741;1.53685648;-0.13805035;0.33024732;1.92127012;0.0419371;0.56501016,0.44828089;2.58001467;1.76953741;0.25491613;-1.05380505;-0.76955115;2.01517453;1.82023926;0.74845256;-0.63548801;-1.21891974;0.25909763;2.32483418;2.27233389;-0.32075726;-0.89502066;-0.49072579;1.67377576;1.92731969;0.43740551;-0.90638538;-0.57214948;0.97286224;2.29571891;1.51735186;-0.80256656;-1.64401788;-0.31295736;1.69228611;2.28647217,0\n"
     ]
    }
   ],
   "source": [
    "!head -3 data/labeled_val_mixed_seq.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.10633131;2.35890751;0.21516641;-1.48749764;-0.33182135;2.76380912;0.76172598;-1.64222243;0.03972318;2.01389863;1.12575525;-1.35875099;-0.32260898;2.33101581;1.70756295;-0.76480232;-1.32256585;2.05513708;1.60825475;-0.49906715;-0.71572314;-20.43073877;-26.73536878;-13.21220398;11.88709738;12.12825952;-16.14151929;-4.44636077;-13.60742667;-8.77741208,9.38762417e-01;2.30372454e+00;1.19213824e+00;-1.40584522e+00;-1.19225511e+00;1.02172179e+00;2.40637359e+00;8.85761949e-01;-1.44288715e+00;-9.37138452e-02;1.49464188e+00;2.37185618e+00;2.20479138e-01;-1.63577451e+00;2.76114695e-03;1.88053771e+00;1.84829242e+00;-9.79075005e-01;-9.65204952e-01;1.16391968e+00;2.68742436e+00;6.56511251e+00;1.86428909e+01;3.72752503e+00;2.50344841e+01;-2.09187849e+01;6.37680923e+00;-9.67410952e+00;8.94896009e+00;-3.01818271e+01,0.59941717;1.58018002;0.891199;-0.85600955;-0.40297109;1.80832898;1.98369483;-0.44282638;-0.57535146;1.12737548;1.85002539;0.94217696;-0.9478323;-0.66152416;1.99606653;1.39923995;-0.14811172;-0.7009864;1.06041257;1.46454321;0.8512644;-22.72885295;-3.42140645;-27.73102877;23.62731692;-1.14700825;-9.98716181;10.71991057;36.79949697;-4.7119051,0.6576432;1.80178982;-0.20362993;0.06228671;1.3198808;0.38908389;-0.83964755;1.13383118;1.02910776;-0.40260263;1.01623412;0.87075091;-0.50651655;0.69288653;1.29364675;-0.0729917;0.28665093;1.70278255;-0.2653048;-0.3482015;1.13022098;-5.87602722;7.86892635;-12.47204179;-10.9334642;5.71306187;2.33644077;20.40833556;-2.01491071;6.92357987,0.98695682;1.75198654;1.54842316;-0.27734611;-1.35580826;-0.56329284;1.50376615;1.9225904;1.2039167;-1.45131997;-0.83726494;0.22331036;2.20502738;1.31182021;-0.19208645;-1.72928371;0.04282549;2.00267934;2.37116529;1.08215624;-1.10202421;-18.02156218;-8.873925;-35.06699001;24.96738862;3.21701384;-17.51106757;3.75665179;17.63942209;36.88782492\n",
      "0.79909927;2.54822784;0.51888991;-1.59581351;0.32715236;1.98838923;1.01122933;-1.25574871;-0.04899049;2.24087223;1.22696515;-0.74238021;-0.57687472;1.72462613;1.49699903;-1.25575438;-1.09133792;2.1878723;1.69005321;-0.20927785;-1.38910362;28.33860371;-40.64431891;-8.98820663;-26.12280332;-15.61491494;-35.77223995;5.5012991;22.02096556;-3.93824752,2.75487284e-02;2.56586740e+00;1.81373952e+00;-8.18474867e-01;-1.06673490e+00;1.40630788e+00;2.16461483e+00;7.00064349e-01;-8.42216252e-01;-5.86252179e-01;2.28954881e+00;1.78824909e+00;1.46964408e-02;-1.11919158e+00;-2.14153651e-01;1.84944170e+00;1.98150960e+00;-1.07022090e+00;-8.74767988e-01;6.59251162e-01;2.78817720e+00;5.91813602e+00;-1.21427837e+01;8.89321312e+00;-2.83757381e+01;-3.27934148e+01;-7.05000834e+00;-1.35194353e+01;-6.38956366e+00;2.50606599e+01,0.48049654;1.81304754;0.80012093;-1.19952797;-0.66767505;1.77995514;1.20774415;-0.41951971;-0.79404802;0.59485236;1.8033307;0.76641914;-0.97514064;-0.23881435;1.8384302;1.63832688;-0.78434913;-0.45003149;1.19669251;1.86982015;0.50955893;11.87750895;-4.25684952;-28.6797325;17.83474825;-4.59683503;4.9823963;-18.1004601;27.00494387;3.37286075,0.31875372;1.66344947;0.05129965;-0.12332377;1.93055251;0.67640704;-0.43215795;1.19355549;0.51409314;-0.5119532;1.43027371;1.3889149;-0.96514737;0.16457016;1.55395379;-0.19606745;0.1386105;1.81187618;0.46164741;0.04957342;1.19795415;-8.59326099;-10.82086973;-12.62587617;-16.72080595;11.72856581;11.03532418;-25.95855938;4.13745485;-1.99548906,0.42181152;2.59453959;1.81918094;-0.50743596;-1.40501365;-0.08087478;2.03492651;1.91329802;0.74359848;-1.28540584;-1.0451629;0.50472866;1.83547105;2.19608571;0.06106719;-1.61844136;-0.10558189;1.55806322;2.71825462;0.40029945;-1.55035731;18.12529328;-12.57208382;18.05514773;-33.79743216;-2.44128673;-22.76197568;-9.53816032;33.7448891;-31.01652764\n",
      "0.83973404;2.70567641;0.64969819;-1.86910713;0.49288056;2.23448537;1.12751409;-1.24570985;0.25442609;2.2349942;1.16502505;-0.83768593;-0.79036278;2.27623113;1.76418871;-1.3067785;-1.1606444;1.58612673;2.09762914;-0.59725926;-0.67566541;1.50984811;1.99529775;-0.70549023;-1.03905216;0.79471094;1.96629312;0.36566912;-1.63211055;1.14672059,0.19987393;2.02283214;1.31034372;-1.14274804;-0.71238907;1.59939006;1.97700972;0.99073529;-1.23240511;-0.7214693;1.37747984;2.4318957;-0.04587039;-1.62359665;-0.15133594;1.91936683;1.52461876;-0.26552098;-0.7252852;1.15330152;2.77702652;1.11904809;-1.53388192;-0.7687273;2.02964158;2.32039025;0.4775162;-1.03742249;-0.17630901;2.45395833,0.52309423;2.31575547;1.17031288;-0.84167261;-0.43445009;1.36844703;1.99634386;-0.05797207;-0.90634685;0.66445725;2.16159911;0.52020431;-1.24711982;-0.17372753;1.15439083;1.73798756;0.00379183;-0.7765985;0.70576935;1.64854076;0.67625135;-0.82589621;0.40954366;1.7685271;1.68906583;-0.25767457;-0.51459697;1.59659195;1.47038243;0.55698869,0.802209;1.15929496;-0.09523979;0.48975406;1.57877228;0.51432618;-0.57263706;1.56209179;1.13530416;-0.76794298;0.90962813;1.01107847;-0.95589571;0.0708867;1.6403418;-0.05724098;-0.05737451;2.0498642;0.54961435;-0.44659979;1.2339419;0.66841731;-0.54764019;1.22015469;1.35630927;-0.5853611;0.61397396;1.55116015;-0.55095091;-0.02059311,0.85056052;2.66099692;1.9251233;0.43030779;-1.23632883;0.03846245;1.78176103;2.76855624;1.13610937;-1.24172193;-1.11414188;0.4021905;2.62968299;1.48377315;0.0893948;-1.1456117;0.02197815;1.52772022;2.64655153;1.0617662;-0.85748062;-1.28499774;1.27214131;1.85526553;1.50494482;-0.57480084;-1.6500711;-0.62289914;2.19319263;2.14675695\n"
     ]
    }
   ],
   "source": [
    "!head -3 data/unlabeled_val_mixed_seq.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.09318005;1.92966839;0.64609906;-1.43418066;0.61212887;2.71039485;1.06367445;-1.10791137;-0.39784441;2.43140035;1.82229656;-1.57219159;-0.07574367;1.88876193;2.14605622;-0.87162418;-1.33054281;2.02344086;2.13922109;-0.44283513;-0.65562326;1.33798303;2.62805909;-0.07076718;-1.29910869;0.98173276;1.99986846;0.42238945;-1.71468468;0.91111554,0.51019374;1.8246647;1.68057444;-0.50818155;-1.19217653;1.37695004;2.59170695;0.35729793;-1.50084934;-0.16332889;1.62237821;1.70763214;-0.52862107;-1.00701877;0.41601169;2.70971923;1.24609059;-1.06182853;-1.35081991;1.33314209;2.22443154;0.56238466;-1.08014025;-0.20057689;1.63070457;1.96626031;0.52362759;-1.17738411;-0.30667785;2.33046763,0.70514017;1.87180165;1.40706487;-0.4040482;-0.4405024;1.7841877;1.33337458;0.08392378;-0.48805024;0.48013054;1.56736799;0.24461322;-1.27466362;-0.03692955;1.90224685;1.63210455;-0.54175341;-0.78912984;0.53334307;1.62459812;0.06206091;-1.34397159;-0.44983121;2.08434339;1.27233575;-0.79436886;-0.3002077;1.48594887;1.78186577;0.59598254,0.31853005;1.68521148;0.24979938;0.38357977;2.0564707;-0.20381951;-0.84646424;1.86238399;1.09401973;-0.17963205;1.01342747;1.00271691;-0.26472773;0.226446;1.37497967;-0.36761185;0.2074163;2.05995985;0.12168396;-0.37331021;1.17814152;0.26303192;-0.71299754;0.82552753;1.14468261;-0.92736903;0.31542476;1.56108894;-0.31644349;0.52447935,0.72803266;2.31609033;1.87728982;-0.03004534;-1.12997878;-0.34905197;1.26767102;2.61774584;0.57929864;-1.45479581;-1.06030222;0.27302319;2.62051931;2.23246588;-0.40506892;-1.08548547;-0.49197376;1.21042474;2.07756531;0.57526929;-1.34985522;-0.73886386;1.14332631;2.03292182;1.6415798;-0.51500086;-1.7398269;0.26296427;1.69670555;1.73076837,0\n",
      "0.0972825;2.54706404;0.70485404;-1.68914261;-0.0388453;2.11807912;0.83406595;-1.0656356;0.22627161;2.31877611;0.91345566;-0.84217773;-1.05334909;1.77226185;1.74347144;-1.40015302;-0.65031579;2.23215246;1.91958005;-0.52674604;-1.18959091;1.11734118;1.81271048;-0.34522045;-1.73452763;0.70195575;2.10354014;0.28034421;-1.23786151;1.07942528,0.62909566;2.21020089;1.16807012;-0.95920875;-1.00638447;1.30110949;2.55954591;0.29803653;-1.70296285;-0.02972602;1.4887455;2.27801434;-0.26849226;-1.68999522;0.39987232;2.30368893;1.31923746;-0.75753561;-1.19251855;1.40176006;2.1174679;0.79639618;-1.50161739;-0.59647446;1.24325031;2.65397153;0.00936053;-1.279235;-0.18089105;1.96792525,0.88448965;2.06564932;0.50550957;-0.89656053;-0.64975076;1.9929169;2.09887012;-0.35071954;-0.98875966;1.16183134;2.03286473;0.28768539;-1.29527549;-0.49041295;1.55543764;1.79266297;-0.38277636;-0.3803683;1.40506202;2.3939914;0.85386625;-0.94423487;0.36417058;2.19391354;1.38951122;-0.19739325;-0.40319117;1.31292343;1.68398229;0.01136725,0.02445266;1.50800659;-0.42042244;0.04180319;1.14571553;0.56247098;-0.59190165;1.67121276;0.903389;-0.46191906;0.62091998;0.93860032;-0.67518963;0.12557419;1.05397464;-0.73024348;-0.19001738;1.6057878;-0.00905631;-0.63688931;1.85914882;0.40892049;-0.36867845;1.51112379;0.63051824;-0.7502769;0.70362529;1.11449771;-0.35143128;-0.13129643,0.91801135;2.36685263;2.05348206;0.05684246;-1.41926677;-0.25124288;1.92139212;1.92261435;1.02279038;-0.91697797;-0.82936617;0.63130643;2.56517433;2.06465107;0.26702097;-1.75352671;0.18469906;2.10919586;2.24910461;0.85200434;-1.26162499;-0.80526146;1.24305762;2.29464624;1.20270919;-0.20465584;-1.60963315;-0.59512998;1.49597639;2.34281598,0\n",
      "0.72345617;2.36235497;0.45648405;-1.03530709;0.16419316;2.49040374;0.67413795;-1.59558758;-0.46860386;1.76520927;1.14496148;-1.32049661;-0.3597191;2.0871276;1.5326144;-0.551078;-0.7463444;1.58765775;1.70415458;-0.74449621;-1.0042505;10.76224294;-41.58751502;-10.36152967;20.27106026;21.91443871;-24.29666902;-1.89553591;-9.95998138;-5.27986212,1.22616114e-02;2.20939044e+00;1.46248674e+00;-5.34152338e-01;-8.97493392e-01;1.43883399e+00;2.73363670e+00;3.09306675e-01;-8.20083513e-01;-2.69514332e-01;2.25924304e+00;2.48367731e+00;-4.09320344e-01;-1.39874304e+00;2.92560192e-01;2.46949837e+00;1.42067224e+00;-3.78255639e-01;-8.16379942e-01;4.65474416e-01;2.20278714e+00;-2.27671551e+01;-1.38425954e+01;-4.32472808e+00;-4.02142849e+01;2.57378535e+01;2.95497487e+00;-2.54702129e+01;5.49780895e-01;2.54929191e+01,2.15002507e-02;2.21672609e+00;1.38381013e+00;-7.37794177e-01;1.22617644e-03;1.66849994e+00;1.53418942e+00;1.65045767e-01;-9.65224352e-01;3.61061922e-01;2.24481649e+00;7.35285549e-01;-9.29171001e-01;-4.76935102e-01;1.53580849e+00;1.53667534e+00;-7.05349771e-01;-1.10146871e+00;7.28777996e-01;1.82768820e+00;8.03918453e-01;8.98499115e+00;-2.60633987e+00;3.05114450e+01;2.80480419e+01;1.28498074e+01;1.64802276e+01;1.78317641e+01;3.05397963e+01;1.46423645e+01,5.55135865e-01;1.76213518e+00;-2.85812933e-02;-2.69707015e-01;2.03014469e+00;2.26009702e-01;-8.57817636e-01;1.85222760e+00;3.77357290e-01;-8.70937680e-01;7.40034915e-01;8.79708844e-01;-5.80754571e-01;4.93462071e-01;9.86292959e-01;-2.48762262e-01;3.25450521e-01;1.72844100e+00;7.89643025e-02;-4.48051221e-01;9.55000857e-01;-3.09180831e+00;-2.45171225e+00;-1.55667949e+01;1.17583960e+01;-6.43320750e+00;-1.65025894e+01;-3.69595119e+01;4.80992166e+00;3.08486851e-01,5.63682831e-01;1.89388029e+00;2.27139157e+00;-5.31383005e-01;-9.10413784e-01;-7.92255526e-01;1.57879433e+00;1.82937004e+00;1.39083709e+00;-1.04607908e+00;-8.14484873e-01;3.86416219e-01;1.78015745e+00;2.20447221e+00;2.23207439e-03;-1.67710351e+00;1.36033352e-01;1.91819287e+00;2.25713476e+00;6.29365744e-01;-1.43032109e+00;1.38603132e+01;2.13578832e+01;3.82719146e+01;-2.44139904e+01;5.43634642e-01;-1.16150933e+01;-9.12706592e+00;-2.30395127e+01;-3.03263452e+01,1\n"
     ]
    }
   ],
   "source": [
    "!head -3 data/labeled_test_mixed_seq.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.83861721;1.99823299;0.98423548;-1.84282336;0.42974863;2.29073027;1.1188934;-1.05186854;-0.58851592;2.48330505;1.25082;-1.48089799;-0.24273682;2.55303002;1.26559778;-1.21403872;-0.9517369;1.74262891;2.22238201;-0.92383485;-1.47574902;1.20534335;2.26636366;-0.04995114;-0.93674628;1.371989;1.98042189;0.20818436;-1.42340607;0.59488468,0.48503988;2.42965796;0.9327926;-0.4136638;-1.01223952;0.71928205;2.65365194;0.60401179;-0.80588773;-0.68824828;1.34036131;2.03758519;0.3059226;-1.40246316;-0.0345257;2.12661406;1.68230132;-0.22649953;-0.99394658;0.74087737;2.20351241;0.66621681;-0.89917979;-0.52148618;1.82835468;1.80670843;0.44879286;-1.39864147;0.39890359;1.68735066,0.08519913;1.43318307;1.10148748;-1.25010978;-0.37692848;1.02965298;1.99770713;0.27480346;-0.45087998;0.76539017;1.49941421;0.69847887;-1.23193202;-0.54937379;1.2760564;1.19704164;-0.06962987;-0.98818571;1.36230535;2.33606584;1.01888233;-1.19800273;-0.0664862;2.22696862;1.38547935;-0.18586824;-0.73459922;1.02661271;2.06164477;0.21493298,0.46227649;1.07393696;-0.17065125;-0.20262615;1.92426011;0.42126963;-0.23776769;0.95746321;0.41075561;-0.60604119;1.03770201;1.43721414;-0.82183784;0.35604321;1.55990164;-0.28043836;0.55816482;1.54953031;0.57242582;-0.54041619;1.3635992;1.15094255;-0.06408001;1.4692054;0.96775178;-0.1192959;0.88618829;1.19183914;-0.6364379;0.14911467,0.60101637;1.93763766;2.03262775;0.04610651;-1.25131875;-0.29544837;1.97319737;2.80258477;0.53618788;-0.70686483;-1.22252389;0.99615801;1.7573349;2.03347845;0.01286345;-1.01825426;-0.43816825;1.36478864;1.83005511;1.2024665;-0.84645763;-1.35552925;1.21850659;2.22919366;1.91661685;-0.33680274;-1.3430539;-0.18629434;2.02472963;2.18925372\n",
      "0.84337914;2.66157136;1.03108497;-1.70946949;0.50454096;2.4205493;1.33577696;-1.01819141;-0.08574799;2.22671758;0.94888564;-1.42159089;-1.02251493;1.95340291;1.39307435;-1.4428404;-0.94754917;1.98423711;1.91378492;-0.85298876;-1.02740974;-22.26056157;-24.03611686;7.3867613;-17.92133218;-8.54811721;45.80259636;3.63416351;-20.3673082;-14.11448769,0.7463867;2.01554874;1.17415246;-0.43753597;-0.86295792;1.17205356;1.84326199;0.43427843;-0.94504219;-0.74074644;1.78209623;2.54207093;-0.27116056;-1.61393216;-0.11859631;2.70316094;1.07882583;-0.98109033;-1.00669094;1.2262755;2.29250701;20.91360955;-31.17269614;4.25708539;-20.97643882;23.27701403;3.90961752;22.63206054;-0.44513466;38.87612527,2.84911868e-01;2.18730552e+00;1.42524016e+00;-4.04777134e-01;8.98711379e-06;1.32623275e+00;1.66804714e+00;3.22023730e-01;-7.17086310e-01;8.48253472e-01;2.26487170e+00;5.42694267e-01;-1.24472003e+00;-2.54440675e-01;1.77094088e+00;1.46755837e+00;6.35346959e-03;-5.08248687e-01;8.48292791e-01;1.89251874e+00;5.81721660e-01;-1.26619790e+01;-5.14000561e+00;-1.73519693e+01;-1.52719907e+01;3.57768090e+00;1.58903375e+01;-1.44679905e+01;2.38959741e+01;-7.16928335e+00,0.18002372;1.25055454;0.1095865;0.05014017;1.6931783;0.15003741;-0.41531504;1.24191806;0.53434224;-0.86099877;1.18008847;0.70812237;-0.10505506;0.59211007;1.40829549;-0.52116387;0.27821585;1.84414591;0.28725243;-0.44676687;1.62870331;9.30857505;-3.96061068;14.99614911;8.13164227;-7.69190655;4.7044942;-17.24811861;-5.98087785;-4.4017642,0.21837062;2.66854677;1.44619603;0.26186473;-1.1881971;-0.21993747;1.96656387;2.27993988;0.93607887;-0.88838787;-1.4409773;0.67499969;2.19690588;1.54060505;0.17675556;-1.72687261;-0.52199435;1.53360785;2.63402379;0.47485175;-0.80842998;-13.29401457;-12.01976925;42.31656745;33.33875151;1.59240568;14.26652699;-10.43688004;27.64139463;-26.73339751\n",
      "8.76576359e-03;2.74090251e+00;2.60010928e-01;-1.30448752e+00;3.31278574e-01;2.10292135e+00;1.20455508e+00;-1.46480662e+00;-5.16169558e-01;2.73545681e+00;1.61932424e+00;-1.38256014e+00;-5.06222389e-01;1.92842775e+00;1.54446601e+00;-1.42555353e+00;-6.02004587e-01;1.59770885e+00;1.52902477e+00;-5.11155342e-01;-6.16169070e-01;1.25019679e+00;2.39241165e+00;-3.47603350e-01;-1.75431490e+00;1.65952650e+00;2.78814477e+00;-1.64802695e-04;-1.19028584e+00;1.30501979e+00,0.42319881;2.71081867;1.05420241;-0.76276111;-0.71768714;1.16344926;2.59503901;0.60933921;-1.0729575;-0.26631233;1.62395064;1.88875651;-0.04798189;-0.88641909;-0.04601791;2.35453803;1.59742783;-0.26409457;-1.60868824;0.97637292;2.20455792;0.69643928;-0.76024038;-1.02275415;1.38065218;2.15522802;-0.02227007;-1.57793859;-0.45315321;1.91163819,0.96901155;1.70275479;1.06826308;-0.2843896;-0.27595994;1.99718128;2.05735022;0.07335209;-0.82844508;0.76517588;2.15318437;0.39120053;-0.6857831;-0.62571362;1.5728682;2.02302932;-0.56092016;-0.90611665;0.4246995;1.49656858;0.19623746;-1.34943357;0.06775065;1.47701665;1.26761254;-0.47599377;-0.32340855;1.59930185;1.46518643;0.76421916,0.58160546;1.50943025;-0.32913305;-0.05771933;1.11403468;-0.03936196;0.12008651;1.63020503;0.98382522;-0.8736822;0.5712447;0.80924014;-0.44663992;0.85249801;1.91120008;-0.71275996;0.36622583;1.51420162;0.37982735;0.11648165;1.58134967;1.07655033;-0.32525771;0.75138399;1.3138952;-0.35757692;0.73588629;1.42495695;-0.1139254;0.30070358,0.57577635;2.33598657;2.05125942;-0.0737995;-0.91485365;-0.90896549;1.28027698;1.88235259;1.08782617;-0.53019267;-0.71855335;0.27790682;1.86166228;1.47729575;-0.67130154;-1.54817327;-0.43490363;1.2172608;2.52085733;0.93090785;-0.75796656;-1.41545132;1.13335111;1.83250375;1.32502991;-0.62459693;-1.56065727;-0.08611623;1.41855872;2.59690371\n"
     ]
    }
   ],
   "source": [
    "!head -3 data/unlabeled_test_mixed_seq.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Local Development"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## input.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Input function functions\n",
    "def split_and_convert_string(string_tensor):\n",
    "  \"\"\"Splits and converts string tensor into dense float tensor.\n",
    "\n",
    "  Given string tensor, splits string by delimiter, converts to and returns\n",
    "  dense float tensor.\n",
    "\n",
    "  Args:\n",
    "    string_tensor: tf.string tensor.\n",
    "\n",
    "  Returns:\n",
    "    tf.float64 tensor split along delimiter.\n",
    "  \"\"\"\n",
    "  # Split string tensor into a sparse tensor based on delimiter\n",
    "  split_string = tf.string_split(source=tf.expand_dims(\n",
    "      input=string_tensor, axis=0), delimiter=\";\")\n",
    "\n",
    "  # Converts the values of the sparse tensor to floats\n",
    "  converted_tensor = tf.string_to_number(\n",
    "      string_tensor=split_string.values,\n",
    "      out_type=tf.float64)\n",
    "\n",
    "  # Create a new sparse tensor with the new converted values,\n",
    "  # because the original sparse tensor values are immutable\n",
    "  new_sparse_tensor = tf.SparseTensor(\n",
    "      indices=split_string.indices,\n",
    "      values=converted_tensor,\n",
    "      dense_shape=split_string.dense_shape)\n",
    "\n",
    "  # Create a dense tensor of the float values that were converted from text csv\n",
    "  dense_floats = tf.sparse_tensor_to_dense(\n",
    "      sp_input=new_sparse_tensor, default_value=0.0)\n",
    "\n",
    "  dense_floats_vector = tf.squeeze(input=dense_floats, axis=0)\n",
    "\n",
    "  return dense_floats_vector\n",
    "\n",
    "\n",
    "def convert_sequences_from_strings_to_floats(features, column_list, seq_len):\n",
    "  \"\"\"Converts sequences from single strings to a sequence of floats.\n",
    "\n",
    "  Given features dictionary and feature column names list, convert features\n",
    "  from strings to a sequence of floats.\n",
    "\n",
    "  Args:\n",
    "    features: Dictionary of tensors of our features as tf.strings.\n",
    "    column_list: List of column names of our features.\n",
    "    seq_len: Number of timesteps in sequence.\n",
    "\n",
    "  Returns:\n",
    "    Dictionary of tensors of our features as tf.float64s.\n",
    "  \"\"\"\n",
    "  for column in column_list:\n",
    "    features[column] = split_and_convert_string(features[column])\n",
    "    # Since we know the sequence length, set the shape to remove the ambiguity\n",
    "    features[column].set_shape([seq_len])\n",
    "\n",
    "  return features\n",
    "\n",
    "\n",
    "def decode_csv(value_column, mode, batch_size, params):\n",
    "  \"\"\"Decodes CSV file into tensors.\n",
    "\n",
    "  Given single string tensor, sequence length, and number of features,\n",
    "  returns features dictionary of tensors and labels tensor.\n",
    "\n",
    "  Args:\n",
    "    value_column: tf.string tensor of shape () compromising entire line of\n",
    "      CSV file.\n",
    "    mode: The estimator ModeKeys. Can be TRAIN or EVAL.\n",
    "    batch_size: Number of examples per batch.\n",
    "    params: Dictionary of user passed parameters.\n",
    "\n",
    "  Returns:\n",
    "    Features dictionary of tensors and labels tensor.\n",
    "  \"\"\"\n",
    "  if (mode == tf.estimator.ModeKeys.TRAIN or\n",
    "      (mode == tf.estimator.ModeKeys.EVAL and\n",
    "       (params[\"training_mode\"] != \"tune_anomaly_thresholds\" or\n",
    "        (params[\"training_mode\"] == \"tune_anomaly_thresholds\" and\n",
    "         not params[\"labeled_tune_thresh\"])))):\n",
    "    # For subset of CSV files that do NOT have labels\n",
    "    columns = tf.decode_csv(\n",
    "        records=value_column,\n",
    "        record_defaults=params[\"feat_defaults\"],\n",
    "        field_delim=\",\")\n",
    "\n",
    "    features = dict(zip(params[\"feat_names\"], columns))\n",
    "    features = convert_sequences_from_strings_to_floats(\n",
    "        features=features,\n",
    "        column_list=params[\"feat_names\"],\n",
    "        seq_len=params[\"seq_len\"])\n",
    "\n",
    "    return features\n",
    "  else:\n",
    "    # For subset of CSV files that DO have labels\n",
    "    columns = tf.decode_csv(\n",
    "        records=value_column,\n",
    "        record_defaults=params[\"feat_defaults\"] + [[0.0]],  # add label default\n",
    "        field_delim=\",\")\n",
    "\n",
    "    features = dict(zip(params[\"feat_names\"] + [\"anomalous_sequence_flag\"], columns))\n",
    "\n",
    "    labels = tf.cast(x=features.pop(\"anomalous_sequence_flag\"), dtype=tf.float64)\n",
    "\n",
    "    features = convert_sequences_from_strings_to_floats(\n",
    "        features=features,\n",
    "        column_list=params[\"feat_names\"],\n",
    "        seq_len=params[\"seq_len\"])\n",
    "\n",
    "    return features, labels\n",
    "\n",
    "\n",
    "def read_dataset(filename, mode, batch_size, params):\n",
    "  \"\"\"Reads CSV time series dataset using tf.data, doing necessary preprocessing.\n",
    "\n",
    "  Given filename, mode, batch size and other parameters, read CSV dataset using\n",
    "  Dataset API, apply necessary preprocessing, and return an input function to\n",
    "  the Estimator API.\n",
    "\n",
    "  Args:\n",
    "    filename: The file pattern that we want to read into our tf.data dataset.\n",
    "    mode: The estimator ModeKeys. Can be TRAIN or EVAL.\n",
    "    batch_size: Number of examples per batch.\n",
    "    params: Dictionary of user passed parameters.\n",
    "\n",
    "  Returns:\n",
    "    An input function.\n",
    "  \"\"\"\n",
    "  def _input_fn():\n",
    "    \"\"\"Wrapper input function to be used by Estimator API to get data tensors.\n",
    "\n",
    "    Returns:\n",
    "      Batched dataset object of dictionary of feature tensors and label tensor.\n",
    "    \"\"\"\n",
    "\n",
    "    # Create list of files that match pattern\n",
    "    file_list = tf.gfile.Glob(filename=filename)\n",
    "\n",
    "    # Create dataset from file list\n",
    "    dataset = tf.data.TextLineDataset(filenames=file_list)  # Read text file\n",
    "\n",
    "    # Decode the CSV file into a features dictionary of tensors\n",
    "    dataset = dataset.map(\n",
    "        map_func=lambda x: decode_csv(\n",
    "            value_column=x, mode=mode, batch_size=batch_size, params=params))\n",
    "\n",
    "    # Determine amount of times to repeat file if we are training or evaluating\n",
    "    if mode == tf.estimator.ModeKeys.TRAIN:\n",
    "      num_epochs = None  # indefinitely\n",
    "    else:\n",
    "      num_epochs = 1  # end-of-input after this\n",
    "\n",
    "    # Repeat files num_epoch times\n",
    "    dataset = dataset.repeat(count=num_epochs)\n",
    "\n",
    "    # Group the data into batches\n",
    "    dataset = dataset.batch(batch_size=batch_size)\n",
    "\n",
    "    # Determine if we should shuffle based on if we are training or evaluating\n",
    "    if mode == tf.estimator.ModeKeys.TRAIN:\n",
    "      dataset = dataset.shuffle(buffer_size=10 * batch_size)\n",
    "\n",
    "    # Create a iterator, then pull batch of features from the example queue\n",
    "    batched_dataset = dataset.make_one_shot_iterator().get_next()\n",
    "\n",
    "    return batched_dataset\n",
    "\n",
    "  return _input_fn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "def try_out_input_function():\n",
    "  \"\"\"Trys out input function for testing purposes.\"\"\"\n",
    "  filename = \"data/labeled_val_mixed_seq.csv\"\n",
    "  training_mode = \"tune_anomaly_thresholds\"\n",
    "  labeled_tune_thresh = True\n",
    "  with tf.Session() as sess:\n",
    "    fn = read_dataset(\n",
    "        filename=filename,\n",
    "        mode=tf.estimator.ModeKeys.EVAL,\n",
    "        batch_size=8,\n",
    "        params={\n",
    "            \"seq_len\": seq_len,\n",
    "            \"training_mode\": training_mode,\n",
    "            \"labeled_tune_thresh\": labeled_tune_thresh})\n",
    "\n",
    "    if training_mode == \"tune_anomaly_thresholds\" and labeled_tune_thresh:\n",
    "      features, labels = sess.run(fn())\n",
    "      print(\"features[tag_0].shape = {}\".format(features[\"tag_0\"].shape))\n",
    "      print(\"labels.shape = {}\".format(labels.shape))\n",
    "      print(\"features = \\n{}\".format(features))\n",
    "      print(\"labels = \\n{}\".format(labels))\n",
    "    else:\n",
    "      features = sess.run(fn())\n",
    "      print(\"features[tag_0].shape = {}\".format(features[\"tag_0\"].shape))\n",
    "      print(\"features = \\n{}\".format(features))\n",
    "\n",
    "  return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "# try_out_input_function()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## autoencoder_dense.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Dense autoencoder model functions\n",
    "def dense_encoder(X, params):\n",
    "  \"\"\"Dense model encoder subgraph that produces latent matrix.\n",
    "\n",
    "  Given data matrix tensor X and dictionary of parameters, process through dense\n",
    "  model encoder subgraph and return encoder latent vector for each example in\n",
    "  batch.\n",
    "\n",
    "  Args:\n",
    "    X: tf.float64 matrix tensor of input data.\n",
    "    params: Dictionary of parameters.\n",
    "\n",
    "  Returns:\n",
    "    tf.float64 matrix tensor encoder latent vector for each example in batch.\n",
    "  \"\"\"\n",
    "  # Create the input layer to our DNN\n",
    "  network = X\n",
    "\n",
    "  # Add hidden layers with the given number of units/neurons per layer\n",
    "  for units in params[\"enc_dnn_hidden_units\"]:\n",
    "    network = tf.layers.dense(\n",
    "        inputs=network,\n",
    "        units=units,\n",
    "        activation=tf.nn.relu)\n",
    "\n",
    "  latent_matrix = tf.layers.dense(\n",
    "      inputs=network,\n",
    "      units=params[\"latent_vector_size\"],\n",
    "      activation=tf.nn.relu)\n",
    "\n",
    "  return latent_matrix\n",
    "\n",
    "\n",
    "def dense_decoder(latent_matrix, orig_dims, params):\n",
    "  \"\"\"Dense model decoder subgraph that produces output matrix.\n",
    "\n",
    "  Given encoder latent matrix tensor, the original dimensions of the input, and\n",
    "  dictionary of parameters, process through dense model decoder subgraph and\n",
    "  return decoder output matrix.\n",
    "\n",
    "  Args:\n",
    "    latent_matrix: tf.float64 matrix tensor of encoder latent matrix.\n",
    "    orig_dims: Original dimensions of input data.\n",
    "    params: Dictionary of parameters.\n",
    "\n",
    "  Returns:\n",
    "    tf.float64 matrix tensor decoder output vector for each example in batch.\n",
    "  \"\"\"\n",
    "  # Create the input layer to our DNN\n",
    "  network = latent_matrix\n",
    "\n",
    "  # Add hidden layers with the given number of units/neurons per layer\n",
    "  for units in params[\"dec_dnn_hidden_units\"][::-1]:\n",
    "    network = tf.layers.dense(\n",
    "        inputs=network,\n",
    "        units=units,\n",
    "        activation=tf.nn.relu)\n",
    "\n",
    "  output_matrix = tf.layers.dense(\n",
    "      inputs=network,\n",
    "      units=orig_dims,\n",
    "      activation=tf.nn.relu)\n",
    "\n",
    "  return output_matrix\n",
    "\n",
    "\n",
    "def dense_autoencoder(X, orig_dims, params):\n",
    "  \"\"\"Dense model autoencoder using dense encoder and decoder networks.\n",
    "\n",
    "  Given data matrix tensor X, the original dimensions of the input, and\n",
    "  dictionary of parameters, process through dense model encoder and decoder\n",
    "  subgraphs and return reconstructed inputs as output.\n",
    "\n",
    "  Args:\n",
    "    X: tf.float64 matrix tensor of input data.\n",
    "    orig_dims: Original dimensions of input data.\n",
    "    params: Dictionary of parameters.\n",
    "\n",
    "  Returns:\n",
    "    tf.float64 matrix tensor decoder output vector for each example in batch\n",
    "    that is the reconstructed inputs.\n",
    "  \"\"\"\n",
    "  latent_matrix = dense_encoder(X, params)\n",
    "  output_matrix = dense_decoder(latent_matrix, orig_dims, params)\n",
    "\n",
    "  return output_matrix\n",
    "\n",
    "\n",
    "def dense_autoencoder_model(\n",
    "    X, mode, params, cur_batch_size, dummy_var):\n",
    "  \"\"\"Dense autoencoder to reconstruct inputs and minimize reconstruction error.\n",
    "\n",
    "  Given data matrix tensor X, the current Estimator mode, the dictionary of\n",
    "  parameters, and the current batch size, process through dense model encoder\n",
    "  and decoder subgraphs and return reconstructed inputs as output.\n",
    "\n",
    "  Args:\n",
    "    X: tf.float64 matrix tensor of input data.\n",
    "    mode: Estimator ModeKeys. Can take values of TRAIN, EVAL, and PREDICT.\n",
    "    params: Dictionary of parameters.\n",
    "    cur_batch_size: Current batch size, could be partially filled.\n",
    "    dummy_var: Dummy variable used to allow training mode to happen since it\n",
    "      requires a gradient to tie back to the graph dependency.\n",
    "\n",
    "  Returns:\n",
    "    loss: Reconstruction loss.\n",
    "    train_op: Train operation so that Estimator can correctly add to dependency\n",
    "      graph.\n",
    "    X_time: 2D tensor representation of time major input data.\n",
    "    X_time_recon: 2D tensor representation of time major input data.\n",
    "    X_feat: 2D tensor representation of feature major input data.\n",
    "    X_feat_recon: 2D tensor representation of feature major input data.\n",
    "  \"\"\"\n",
    "  # Reshape into 2-D tensors\n",
    "  # Time based\n",
    "  # shape = (cur_batch_size * seq_len, num_feat)\n",
    "  X_time = tf.reshape(\n",
    "      tensor=X,\n",
    "      shape=[cur_batch_size * params[\"seq_len\"], params[\"num_feat\"]])\n",
    "\n",
    "  # shape = (cur_batch_size * seq_len, num_feat)\n",
    "  X_time_recon = dense_autoencoder(X_time, params[\"num_feat\"], params)\n",
    "\n",
    "  # Features based\n",
    "  # shape = (cur_batch_size, num_feat, seq_len)\n",
    "  X_transposed = tf.transpose(a=X, perm=[0, 2, 1])\n",
    "\n",
    "  # shape = (cur_batch_size * num_feat, seq_len)\n",
    "  X_feat = tf.reshape(\n",
    "      tensor=X_transposed,\n",
    "      shape=[cur_batch_size * params[\"num_feat\"], params[\"seq_len\"]])\n",
    "\n",
    "  # shape = (cur_batch_size * num_feat, seq_len)\n",
    "  X_feat_recon = dense_autoencoder(X_feat, params[\"seq_len\"], params)\n",
    "\n",
    "  if (mode == tf.estimator.ModeKeys.TRAIN and\n",
    "      params[\"training_mode\"] == \"reconstruction\"):\n",
    "    X_time_recon_3d = tf.reshape(\n",
    "        tensor=X_time_recon,\n",
    "        shape=[cur_batch_size, params[\"seq_len\"], params[\"num_feat\"]])\n",
    "    X_feat_recon_3d = tf.transpose(\n",
    "        a=tf.reshape(\n",
    "            tensor=X_feat_recon,\n",
    "            shape=[cur_batch_size, params[\"num_feat\"], params[\"seq_len\"]]),\n",
    "        perm=[0, 2, 1])\n",
    "\n",
    "    X_time_recon_3d_weighted = X_time_recon_3d * params[\"time_loss_weight\"]\n",
    "    X_feat_recon_3d_weighted = X_feat_recon_3d * params[\"feat_loss_weight\"]\n",
    "\n",
    "    predictions = (X_time_recon_3d_weighted + X_feat_recon_3d_weighted) \\\n",
    "      / (params[\"time_loss_weight\"] + params[\"feat_loss_weight\"])\n",
    "\n",
    "    loss = tf.losses.mean_squared_error(labels=X, predictions=predictions)\n",
    "\n",
    "    train_op = tf.contrib.layers.optimize_loss(\n",
    "        loss=loss,\n",
    "        global_step=tf.train.get_global_step(),\n",
    "        learning_rate=params[\"learning_rate\"],\n",
    "        optimizer=\"Adam\")\n",
    "\n",
    "    return loss, train_op, None, None, None, None\n",
    "  else:\n",
    "    return None, None, X_time, X_time_recon, X_feat, X_feat_recon"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## autoencoder_lstm.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "# LSTM Encoder-decoder Autoencoder model functions\n",
    "def create_LSTM_stack(lstm_hidden_units, lstm_dropout_output_keep_probs):\n",
    "  \"\"\"Create LSTM stacked cells.\n",
    "\n",
    "  Given list of LSTM hidden units and list of LSTM dropout output keep\n",
    "  probabilities.\n",
    "\n",
    "  Args:\n",
    "    lstm_hidden_units: List of integers for the number of hidden units in each\n",
    "      layer.\n",
    "    lstm_dropout_output_keep_probs: List of floats for the dropout output keep\n",
    "      probabilities for each layer.\n",
    "\n",
    "  Returns:\n",
    "    MultiRNNCell object of stacked LSTM layers.\n",
    "  \"\"\"\n",
    "  # First create a list of LSTM cell objects using our list of lstm hidden\n",
    "  # unit sizes\n",
    "  lstm_cells = [tf.contrib.rnn.BasicLSTMCell(\n",
    "      num_units=units,\n",
    "      forget_bias=1.0,\n",
    "      state_is_tuple=True)\n",
    "                for units in lstm_hidden_units]\n",
    "\n",
    "  # Next apply a dropout wrapper to our stack of LSTM cells,\n",
    "  # in this case just on the outputs\n",
    "  dropout_lstm_cells = [tf.nn.rnn_cell.DropoutWrapper(\n",
    "      cell=lstm_cells[cell_index],\n",
    "      input_keep_prob=1.0,\n",
    "      output_keep_prob=lstm_dropout_output_keep_probs[cell_index],\n",
    "      state_keep_prob=1.0)\n",
    "                        for cell_index in range(len(lstm_cells))]\n",
    "\n",
    "  # Create a stack of layers of LSTM cells\n",
    "  # Combines list into MultiRNNCell object\n",
    "  stacked_lstm_cells = tf.contrib.rnn.MultiRNNCell(\n",
    "      cells=dropout_lstm_cells,\n",
    "      state_is_tuple=True)\n",
    "\n",
    "  return stacked_lstm_cells\n",
    "\n",
    "\n",
    "# The rnn_decoder function takes labels during TRAIN/EVAL\n",
    "# and a start token followed by its previous predictions during PREDICT\n",
    "# Starts with an initial state of the final encoder states\n",
    "def rnn_decoder(dec_input, init_state, cell, infer, dnn_hidden_units, num_feat):\n",
    "  \"\"\"Decoder for RNN cell.\n",
    "\n",
    "  Given list of LSTM hidden units and list of LSTM dropout output keep\n",
    "  probabilities.\n",
    "\n",
    "  Args:\n",
    "    dec_input: List of tf.float64 current batch size by number of features\n",
    "      matrix tensors input to the decoder.\n",
    "    init_state: Initial state of the decoder cell. Final state from the\n",
    "      encoder cell.\n",
    "    cell: RNN Cell object.\n",
    "    infer: Boolean whether in inference mode or not.\n",
    "    dnn_hidden_units: Python list of integers of number of units per DNN layer.\n",
    "    num_feat: Python integer of the number of features.\n",
    "\n",
    "  Returns:\n",
    "    outputs: List of decoder outputs of length number of timesteps of tf.float64\n",
    "      current batch size by number of features matrix tensors.\n",
    "    state: Final cell state of the decoder.\n",
    "  \"\"\"\n",
    "  # Create the decoder variable scope\n",
    "  with tf.variable_scope(\"decoder\"):\n",
    "    # Load in our initial state from our encoder\n",
    "    # Tuple of final encoder c_state and h_state of final encoder layer\n",
    "    state = init_state\n",
    "\n",
    "    # Create an empty list to store our hidden state output for every timestep\n",
    "    outputs = []\n",
    "\n",
    "    # Begin with no previous output\n",
    "    previous_output = None\n",
    "\n",
    "    # Loop over all of our dec_input which will be seq_len long\n",
    "    for index, decoder_input in enumerate(dec_input):\n",
    "      # If there has been a previous output, we will determine the next input\n",
    "      if previous_output is not None:\n",
    "        # Create the input layer to our DNN\n",
    "        # shape = (cur_batch_size, lstm_hidden_units[-1])\n",
    "        network = previous_output\n",
    "\n",
    "        # Create our dnn variable scope\n",
    "        with tf.variable_scope(name_or_scope=\"dnn\", reuse=tf.AUTO_REUSE):\n",
    "          # Add hidden layers with the given number of units/neurons per layer\n",
    "          # shape = (cur_batch_size, dnn_hidden_units[i])\n",
    "          for units in dnn_hidden_units:\n",
    "            network = tf.layers.dense(\n",
    "                inputs=network,\n",
    "                units=units,\n",
    "                activation=tf.nn.relu)\n",
    "\n",
    "          # Connect final hidden layer to linear layer to get the logits\n",
    "          # shape = (cur_batch_size, num_feat)\n",
    "          logits = tf.layers.dense(\n",
    "              inputs=network,\n",
    "              units=num_feat,\n",
    "              activation=None)\n",
    "\n",
    "        # If we are in inference then we will overwrite our next decoder_input\n",
    "        # with the logits we just calculated. Otherwise, we leave the decoder\n",
    "        # input input as it was from the enumerated list. We have to calculate\n",
    "        # the logits even when not using them so that the correct DNN subgraph\n",
    "        # will be generated here and after the encoder-decoder for both\n",
    "        # training and inference\n",
    "        if infer:\n",
    "          # shape = (cur_batch_size, num_feat)\n",
    "          decoder_input = logits\n",
    "\n",
    "      # If this isn\"t our first time through the loop, just reuse(share) the\n",
    "      # same variables for each iteration within the current variable scope\n",
    "      if index > 0:\n",
    "        tf.get_variable_scope().reuse_variables()\n",
    "\n",
    "      # Run the decoder input through the decoder stack picking up from the\n",
    "      # previous state\n",
    "      # output_shape = (cur_batch_size, lstm_hidden_units[-1])\n",
    "      # state_shape = # tuple of final decoder c_state and h_state\n",
    "      output, state = cell(decoder_input, state)\n",
    "\n",
    "      # Append the current decoder hidden state output to the outputs list\n",
    "      # List seq_len long of shape = (cur_batch_size, lstm_hidden_units[-1])\n",
    "      outputs.append(output)\n",
    "\n",
    "      # Set the previous output to the output just calculated\n",
    "      # shape = (cur_batch_size, lstm_hidden_units[-1])\n",
    "      previous_output = output\n",
    "  return outputs, state\n",
    "\n",
    "\n",
    "def lstm_enc_dec_autoencoder_model(\n",
    "    X, mode, params, cur_batch_size, dummy_var):\n",
    "  \"\"\"LSTM autoencoder to reconstruct inputs and minimize reconstruction error.\n",
    "\n",
    "  Given data matrix tensor X, the current Estimator mode, the dictionary of\n",
    "  parameters, current batch size, and the number of features, process through\n",
    "  LSTM model encoder, decoder, and DNN subgraphs and return reconstructed inputs\n",
    "  as output.\n",
    "\n",
    "  Args:\n",
    "    X: tf.float64 matrix tensor of input data.\n",
    "    mode: Estimator ModeKeys. Can take values of TRAIN, EVAL, and PREDICT.\n",
    "    params: Dictionary of parameters.\n",
    "    cur_batch_size: Current batch size, could be partially filled.\n",
    "    dummy_var: Dummy variable used to allow training mode to happen since it\n",
    "      requires a gradient to tie back to the graph dependency.\n",
    "\n",
    "  Returns:\n",
    "    loss: Reconstruction loss.\n",
    "    train_op: Train operation so that Estimator can correctly add to dependency\n",
    "      graph.\n",
    "    X_time: 2D tensor representation of time major input data.\n",
    "    X_time_recon: 2D tensor representation of time major input data.\n",
    "    X_feat: 2D tensor representation of feature major input data.\n",
    "    X_feat_recon: 2D tensor representation of feature major input data.\n",
    "  \"\"\"\n",
    "  # Unstack 3-D features tensor into a sequence(list) of 2-D tensors\n",
    "  # shape = (cur_batch_size, num_feat)\n",
    "  X_sequence = tf.unstack(value=X, num=params[\"seq_len\"], axis=1)\n",
    "\n",
    "  # Since this is an autoencoder, the features are the labels.\n",
    "  # It often works better though to have the labels in reverse order\n",
    "  # shape = (cur_batch_size, seq_len, num_feat)\n",
    "  if params[\"reverse_labels_sequence\"]:\n",
    "    Y = tf.reverse_sequence(\n",
    "        input=X,\n",
    "        seq_lengths=tf.tile(\n",
    "            input=tf.constant(value=[params[\"seq_len\"]], dtype=tf.int64),\n",
    "            multiples=tf.expand_dims(input=cur_batch_size, axis=0)),\n",
    "        seq_axis=1,\n",
    "        batch_axis=0)\n",
    "  else:\n",
    "    Y = X  # shape = (cur_batch_size, seq_len, num_feat)\n",
    "\n",
    "  ##############################################################################\n",
    "\n",
    "  # Create encoder of encoder-decoder LSTM stacks\n",
    "\n",
    "  # Create our decoder now\n",
    "  dec_stacked_lstm_cells = create_LSTM_stack(\n",
    "      params[\"dec_lstm_hidden_units\"],\n",
    "      params[\"lstm_dropout_output_keep_probs\"])\n",
    "\n",
    "  # Create the encoder variable scope\n",
    "  with tf.variable_scope(\"encoder\"):\n",
    "    # Create separate encoder cells with their own weights separate from decoder\n",
    "    enc_stacked_lstm_cells = create_LSTM_stack(\n",
    "        params[\"enc_lstm_hidden_units\"],\n",
    "        params[\"lstm_dropout_output_keep_probs\"])\n",
    "\n",
    "    # Encode the input sequence using our encoder stack of LSTMs\n",
    "    # enc_outputs = seq_len long of shape = (cur_batch_size, enc_lstm_hidden_units[-1])\n",
    "    # enc_states = tuple of final encoder c_state and h_state for each layer\n",
    "    _, enc_states = tf.nn.static_rnn(\n",
    "        cell=enc_stacked_lstm_cells,\n",
    "        inputs=X_sequence,\n",
    "        initial_state=enc_stacked_lstm_cells.zero_state(\n",
    "            batch_size=tf.cast(x=cur_batch_size, dtype=tf.int32),\n",
    "            dtype=tf.float64),\n",
    "        dtype=tf.float64)\n",
    "\n",
    "    # We just pass on the final c and h states of the encoder\"s last layer,\n",
    "    # so extract that and drop the others\n",
    "    # LSTMStateTuple shape = (cur_batch_size, lstm_hidden_units[-1])\n",
    "    enc_final_states = enc_states[-1]\n",
    "\n",
    "    # Extract the c and h states from the tuple\n",
    "    # both have shape = (cur_batch_size, lstm_hidden_units[-1])\n",
    "    enc_final_c, enc_final_h = enc_final_states\n",
    "\n",
    "    # In case the decoder\"s first layer's number of units is different than\n",
    "    # encoder's last layer's number of units, use a dense layer to map to the\n",
    "    # correct shape\n",
    "    # shape = (cur_batch_size, dec_lstm_hidden_units[0])\n",
    "    enc_final_c_dense = tf.layers.dense(\n",
    "        inputs=enc_final_c,\n",
    "        units=params[\"dec_lstm_hidden_units\"][0],\n",
    "        activation=None)\n",
    "\n",
    "    # shape = (cur_batch_size, dec_lstm_hidden_units[0])\n",
    "    enc_final_h_dense = tf.layers.dense(\n",
    "        inputs=enc_final_h,\n",
    "        units=params[\"dec_lstm_hidden_units\"][0],\n",
    "        activation=None)\n",
    "\n",
    "    # The decoder\"s first layer\"s state comes from the encoder,\n",
    "    # the rest of the layers\" initial states are zero\n",
    "    dec_init_states = tuple(\n",
    "        [tf.contrib.rnn.LSTMStateTuple(c=enc_final_c_dense,\n",
    "                                       h=enc_final_h_dense)] + \\\n",
    "        [tf.contrib.rnn.LSTMStateTuple(\n",
    "            c=tf.zeros(shape=[cur_batch_size, units], dtype=tf.float64),\n",
    "            h=tf.zeros(shape=[cur_batch_size, units], dtype=tf.float64))\n",
    "         for units in params[\"dec_lstm_hidden_units\"][1:]])\n",
    "\n",
    "  ##############################################################################\n",
    "\n",
    "  # Create decoder of encoder-decoder LSTM stacks\n",
    "\n",
    "  # Train our decoder now\n",
    "\n",
    "  # Encoder-decoders work differently during training, evaluation, and inference\n",
    "  # so we will have two separate subgraphs for each\n",
    "  if (mode == tf.estimator.ModeKeys.TRAIN and\n",
    "      params[\"training_mode\"] == \"reconstruction\"):\n",
    "    # Break 3-D labels tensor into a list of 2-D tensors\n",
    "    # shape = (cur_batch_size, num_feat)\n",
    "    unstacked_labels = tf.unstack(value=Y, num=params[\"seq_len\"], axis=1)\n",
    "\n",
    "    # Call our decoder using the labels as our inputs, the encoder final state\n",
    "    # as our initial state, our other LSTM stack as our cells, and inference\n",
    "    # set to false\n",
    "    dec_outputs, _ = rnn_decoder(\n",
    "        dec_input=unstacked_labels,\n",
    "        init_state=dec_init_states,\n",
    "        cell=dec_stacked_lstm_cells,\n",
    "        infer=False,\n",
    "        dnn_hidden_units=params[\"dnn_hidden_units\"],\n",
    "        num_feat=params[\"num_feat\"])\n",
    "  else:\n",
    "    # Since this is inference create fake labels. The list length needs to be\n",
    "    # the output sequence length even though only the first element is the only\n",
    "    # one actually used (as our go signal)\n",
    "    fake_labels = [tf.zeros(shape=[cur_batch_size, params[\"num_feat\"]],\n",
    "                            dtype=tf.float64)\n",
    "                   for _ in range(params[\"seq_len\"])]\n",
    "\n",
    "    # Call our decoder using fake labels as our inputs, the encoder final state\n",
    "    # as our initial state, our other LSTM stack as our cells, and inference\n",
    "    # set to true\n",
    "    # dec_outputs = seq_len long of shape = (cur_batch_size, dec_lstm_hidden_units[-1])\n",
    "    # decoder_states = tuple of final decoder c_state and h_state for each layer\n",
    "    dec_outputs, _ = rnn_decoder(\n",
    "        dec_input=fake_labels,\n",
    "        init_state=dec_init_states,\n",
    "        cell=dec_stacked_lstm_cells,\n",
    "        infer=True,\n",
    "        dnn_hidden_units=params[\"dnn_hidden_units\"],\n",
    "        num_feat=params[\"num_feat\"])\n",
    "\n",
    "  # Stack together list of rank 2 decoder output tensors into one rank 3 tensor\n",
    "  # shape = (cur_batch_size, seq_len, lstm_hidden_units[-1])\n",
    "  stacked_dec_outputs = tf.stack(values=dec_outputs, axis=1)\n",
    "\n",
    "  # Reshape rank 3 decoder outputs into rank 2 by folding sequence length into\n",
    "  # batch size\n",
    "  # shape = (cur_batch_size * seq_len, lstm_hidden_units[-1])\n",
    "  reshaped_stacked_dec_outputs = tf.reshape(\n",
    "      tensor=stacked_dec_outputs,\n",
    "      shape=[cur_batch_size * params[\"seq_len\"],\n",
    "             params[\"dec_lstm_hidden_units\"][-1]])\n",
    "\n",
    "  ##############################################################################\n",
    "\n",
    "  # Create the DNN structure now after the encoder-decoder LSTM stack\n",
    "  # Create the input layer to our DNN\n",
    "  # shape = (cur_batch_size * seq_len, lstm_hidden_units[-1])\n",
    "  network = reshaped_stacked_dec_outputs\n",
    "\n",
    "  # Reuse the same variable scope as we used within our decoder (for inference)\n",
    "  with tf.variable_scope(name_or_scope=\"dnn\", reuse=tf.AUTO_REUSE):\n",
    "    # Add hidden layers with the given number of units/neurons per layer\n",
    "    for units in params[\"dnn_hidden_units\"]:\n",
    "      # shape = (cur_batch_size * seq_len, dnn_hidden_units[i])\n",
    "      network = tf.layers.dense(\n",
    "          inputs=network,\n",
    "          units=units,\n",
    "          activation=tf.nn.relu)\n",
    "\n",
    "    # Connect the final hidden layer to a dense layer with no activation to\n",
    "    # get the logits\n",
    "    # shape = (cur_batch_size * seq_len, num_feat)\n",
    "    logits = tf.layers.dense(\n",
    "        inputs=network,\n",
    "        units=params[\"num_feat\"],\n",
    "        activation=None)\n",
    "\n",
    "  # Now that we are through the final DNN for each sequence element for\n",
    "  # each example in the batch, reshape the predictions to match our labels.\n",
    "  # shape = (cur_batch_size, seq_len, num_feat)\n",
    "  predictions = tf.reshape(\n",
    "      tensor=logits,\n",
    "      shape=[cur_batch_size, params[\"seq_len\"], params[\"num_feat\"]])\n",
    "\n",
    "  if (mode == tf.estimator.ModeKeys.TRAIN and\n",
    "      params[\"training_mode\"] == \"reconstruction\"):\n",
    "    loss = tf.losses.mean_squared_error(labels=Y, predictions=predictions)\n",
    "\n",
    "    train_op = tf.contrib.layers.optimize_loss(\n",
    "        loss=loss,\n",
    "        global_step=tf.train.get_global_step(),\n",
    "        learning_rate=params[\"learning_rate\"],\n",
    "        optimizer=\"Adam\")\n",
    "\n",
    "    return loss, train_op, None, None, None, None\n",
    "  else:\n",
    "    if params[\"reverse_labels_sequence\"]:\n",
    "      # shape=(cur_batch_size, seq_len, num_feat)\n",
    "      predictions = tf.reverse_sequence(\n",
    "          input=predictions,\n",
    "          seq_lengths=tf.tile(\n",
    "              input=tf.constant(value=[params[\"seq_len\"]], dtype=tf.int64),\n",
    "              multiples=tf.expand_dims(input=cur_batch_size, axis=0)),\n",
    "          seq_axis=1,\n",
    "          batch_axis=0)\n",
    "\n",
    "    # Reshape into 2-D tensors\n",
    "    # Time based\n",
    "    # shape = (cur_batch_size * seq_len, num_feat)\n",
    "    X_time = tf.reshape(\n",
    "        tensor=X,\n",
    "        shape=[cur_batch_size * params[\"seq_len\"], params[\"num_feat\"]])\n",
    "\n",
    "    X_time_recon = tf.reshape(\n",
    "        tensor=predictions,\n",
    "        shape=[cur_batch_size * params[\"seq_len\"], params[\"num_feat\"]])\n",
    "\n",
    "    # Features based\n",
    "    # shape = (cur_batch_size, num_feat, seq_len)\n",
    "    X_transposed = tf.transpose(a=X, perm=[0, 2, 1])\n",
    "\n",
    "    # shape = (cur_batch_size * num_feat, seq_len)\n",
    "    X_feat = tf.reshape(\n",
    "        tensor=X_transposed,\n",
    "        shape=[cur_batch_size * params[\"num_feat\"], params[\"seq_len\"]])\n",
    "\n",
    "    # shape = (cur_batch_size, num_feat, seq_len)\n",
    "    predictions_transposed = tf.transpose(a=predictions, perm=[0, 2, 1])\n",
    "\n",
    "    # shape = (cur_batch_size * num_feat, seq_len)\n",
    "    X_feat_recon = tf.reshape(\n",
    "        tensor=predictions_transposed,\n",
    "        shape=[cur_batch_size * params[\"num_feat\"], params[\"seq_len\"]])\n",
    "\n",
    "    return None, None, X_time, X_time_recon, X_feat, X_feat_recon"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## autoencoder_pca.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "# PCA model functions\n",
    "def create_pca_vars(var_name, size):\n",
    "  \"\"\"Creates PCA variables.\n",
    "\n",
    "  Given variable name and size, create and return PCA variables for count,\n",
    "  mean, covariance, eigenvalues, eignvectors, and k principal components.\n",
    "\n",
    "  Args:\n",
    "    var_name: String denoting which set of variables to create. Values are\n",
    "      \"time\" and \"feat\".\n",
    "    size: The size of the variable, either sequence length or number of\n",
    "      features.\n",
    "\n",
    "  Returns:\n",
    "    PCA variables for count, mean, covariance, eigenvalues,\n",
    "    eigenvectors, and k principal components.\n",
    "  \"\"\"\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=\"pca_vars\", reuse=tf.AUTO_REUSE):\n",
    "    count_var = tf.get_variable(\n",
    "        name=\"pca_{}_count_var\".format(var_name),\n",
    "        dtype=tf.int64,\n",
    "        initializer=tf.zeros(shape=[], dtype=tf.int64),\n",
    "        trainable=False)\n",
    "\n",
    "    mean_var = tf.get_variable(\n",
    "        name=\"pca_{}_mean_var\".format(var_name),\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(shape=[size], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "    cov_var = tf.get_variable(\n",
    "        name=\"pca_{}_cov_var\".format(var_name),\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(shape=[size, size], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "    eigval_var = tf.get_variable(\n",
    "        name=\"pca_{}_eigval_var\".format(var_name),\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(shape=[size], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "    eigvec_var = tf.get_variable(\n",
    "        name=\"pca_{}_eigvec_var\".format(var_name),\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(shape=[size, size], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "    k_pc_var = tf.get_variable(\n",
    "        name=\"pca_{}_k_principal_components_var\".format(var_name),\n",
    "        dtype=tf.int64,\n",
    "        initializer=tf.ones(shape=[], dtype=tf.int64),\n",
    "        trainable=False)\n",
    "\n",
    "  return count_var, mean_var, cov_var, eigval_var, eigvec_var, k_pc_var\n",
    "\n",
    "\n",
    "def create_both_pca_vars(seq_len, num_feat):\n",
    "  \"\"\"Creates both time & feature major PCA variables.\n",
    "\n",
    "  Given dimensions of inputs, create and return PCA variables for count,\n",
    "  mean, covariance, eigenvalues, eigenvectors, and k principal components\n",
    "  for both time and feature major representations.\n",
    "\n",
    "  Args:\n",
    "    seq_len: Number of timesteps in sequence.\n",
    "    num_feat: Number of features.\n",
    "\n",
    "  Returns:\n",
    "    PCA variables for count, mean, covariance, eigenvalues,\n",
    "    eigenvectors, and k principal components for both time and feature\n",
    "    major representations.\n",
    "  \"\"\"\n",
    "  # Time based\n",
    "  (pca_time_count_var,\n",
    "   pca_time_mean_var,\n",
    "   pca_time_cov_var,\n",
    "   pca_time_eigval_var,\n",
    "   pca_time_eigvec_var,\n",
    "   pca_time_k_pc_var) = create_pca_vars(\n",
    "       var_name=\"time\", size=num_feat)\n",
    "\n",
    "  # Features based\n",
    "  (pca_feat_count_var,\n",
    "   pca_feat_mean_var,\n",
    "   pca_feat_cov_var,\n",
    "   pca_feat_eigval_var,\n",
    "   pca_feat_eigvec_var,\n",
    "   pca_feat_k_pc_var) = create_pca_vars(\n",
    "       var_name=\"feat\", size=seq_len)\n",
    "\n",
    "  return (pca_time_count_var,\n",
    "          pca_time_mean_var,\n",
    "          pca_time_cov_var,\n",
    "          pca_time_eigval_var,\n",
    "          pca_time_eigvec_var,\n",
    "          pca_time_k_pc_var,\n",
    "          pca_feat_count_var,\n",
    "          pca_feat_mean_var,\n",
    "          pca_feat_cov_var,\n",
    "          pca_feat_eigval_var,\n",
    "          pca_feat_eigvec_var,\n",
    "          pca_feat_k_pc_var)\n",
    "\n",
    "\n",
    "def pca_reconstruction_k_pc(X_cen, pca_eigvec_var, k_pc):\n",
    "  \"\"\"PCA reconstruction with k principal components.\n",
    "\n",
    "  Given centered data matrix tensor X, variables for the column means\n",
    "  and eigenvectors, and the number of principal components, returns\n",
    "  the reconstruction of X centered.\n",
    "\n",
    "  Args:\n",
    "    X_cen: tf.float64 matrix tensor of centered input data.\n",
    "    pca_eigvec_var: tf.float64 matrix variable storing eigenvectors.\n",
    "    k_pc: Number of principal components to keep.\n",
    "\n",
    "  Returns:\n",
    "    X_cen_recon: 2D input data tensor reconstructed.\n",
    "  \"\"\"\n",
    "  # time_shape = (num_feat, num_feat)\n",
    "  # feat_shape = (seq_len, seq_len)\n",
    "  projection_matrix = tf.matmul(\n",
    "      a=pca_eigvec_var[:, -k_pc:],\n",
    "      b=pca_eigvec_var[:, -k_pc:],\n",
    "      transpose_b=True)\n",
    "\n",
    "  # time_shape = (cur_batch_size * seq_len, num_feat)\n",
    "  # feat_shape = (cur_batch_size * num_feat, seq_len)\n",
    "  X_cen_recon = tf.matmul(\n",
    "      a=X_cen,\n",
    "      b=projection_matrix)\n",
    "\n",
    "  return X_cen_recon\n",
    "\n",
    "\n",
    "def pca_reconstruction_k_pc_mse(X_cen, pca_eigvec_var, k_pc):\n",
    "  \"\"\"PCA reconstruction with k principal components.\n",
    "\n",
    "  Given centered data matrix tensor X, variables for the column means\n",
    "  and eigenvectors, and the number of principal components, returns\n",
    "  reconstruction MSE.\n",
    "\n",
    "  Args:\n",
    "    X_cen: tf.float64 matrix tensor of centered input data.\n",
    "    pca_eigvec_var: tf.float64 matrix variable storing eigenvectors.\n",
    "    k_pc: Number of principal components to keep.\n",
    "\n",
    "  Returns:\n",
    "    mse: Reconstruction mean squared error.\n",
    "  \"\"\"\n",
    "  # time_shape = (cur_batch_size * seq_len, num_feat)\n",
    "  # feat_shape = (cur_batch_size * num_feat, seq_len)\n",
    "  X_cen_recon = pca_reconstruction_k_pc(\n",
    "      X_cen, pca_eigvec_var, k_pc)\n",
    "\n",
    "  # time_shape = (cur_batch_size * seq_len, num_feat)\n",
    "  # feat_shape = (cur_batch_size * num_feat, seq_len)\n",
    "  error = X_cen - X_cen_recon\n",
    "\n",
    "  # shape = ()\n",
    "  mse = tf.reduce_mean(\n",
    "      input_tensor=tf.reduce_sum(\n",
    "          input_tensor=tf.square(x=error), axis=-1))\n",
    "\n",
    "  return mse\n",
    "\n",
    "\n",
    "def find_best_k_principal_components(X_recon_mse, pca_k_pc_var):\n",
    "  \"\"\"Find best k principal components from reconstruction MSE.\n",
    "\n",
    "  Given reconstruction MSE, return number of principal components\n",
    "  with lowest MSE in varible.\n",
    "\n",
    "  Args:\n",
    "    X_recon_mse: tf.float64 vector tensor of reconstruction mean\n",
    "      squared error.\n",
    "    pca_k_pc_var: tf.int64 scalar variable to hold best number of\n",
    "      principal components.\n",
    "\n",
    "  Returns:\n",
    "    pca_k_pc_var: Updated scalar variable now with best number of\n",
    "      principal components.\n",
    "  \"\"\"\n",
    "  best_pca_k_pc = tf.argmin(input=X_recon_mse) + 1\n",
    "\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[tf.assign(ref=pca_k_pc_var,\n",
    "                                value=best_pca_k_pc)]):\n",
    "\n",
    "    return tf.identity(input=pca_k_pc_var)\n",
    "\n",
    "\n",
    "def set_k_principal_components(user_k_pc, pca_k_pc_var):\n",
    "  \"\"\"Set k principal components from user-defined value.\n",
    "\n",
    "  Given user-defined number of principal components, return\n",
    "  variable set to this value.\n",
    "\n",
    "  Args:\n",
    "    user_k_pc: User-defined python integer for number of principal\n",
    "      components.\n",
    "    pca_k_pc_var: tf.int64 scalar variable to hold chosen number of\n",
    "      principal components.\n",
    "\n",
    "  Returns:\n",
    "    pca_k_pc_var: Updated scalar variable now with chosen number of\n",
    "      principal components.\n",
    "  \"\"\"\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[tf.assign(ref=pca_k_pc_var,\n",
    "                                value=user_k_pc)]):\n",
    "\n",
    "    return tf.identity(input=pca_k_pc_var)\n",
    "\n",
    "\n",
    "def pca_model(X, mode, params, cur_batch_size, dummy_var):\n",
    "  \"\"\"PCA to reconstruct inputs and minimize reconstruction error.\n",
    "\n",
    "  Given data matrix tensor X, the current Estimator mode, the dictionary of\n",
    "  parameters, current batch size, and the number of features, process through\n",
    "  PCA model subgraph and return reconstructed inputs as output.\n",
    "\n",
    "  Args:\n",
    "    X: tf.float64 matrix tensor of input data.\n",
    "    mode: Estimator ModeKeys. Can take values of TRAIN, EVAL, and PREDICT.\n",
    "    params: Dictionary of parameters.\n",
    "    cur_batch_size: Current batch size, could be partially filled.\n",
    "    dummy_var: Dummy variable used to allow training mode to happen since it\n",
    "      requires a gradient to tie back to the graph dependency.\n",
    "\n",
    "  Returns:\n",
    "    loss: Reconstruction loss.\n",
    "    train_op: Train operation so that Estimator can correctly add to dependency\n",
    "      graph.\n",
    "    X_time: 2D tensor representation of time major input data.\n",
    "    X_time_recon: 2D tensor representation of time major input data.\n",
    "    X_feat: 2D tensor representation of feature major input data.\n",
    "    X_feat_recon: 2D tensor representation of feature major input data.\n",
    "  \"\"\"\n",
    "  # Reshape into 2-D tensors\n",
    "  # Time based\n",
    "  # shape = (cur_batch_size * seq_len, num_feat)\n",
    "  X_time = tf.reshape(\n",
    "      tensor=X,\n",
    "      shape=[cur_batch_size * params[\"seq_len\"], params[\"num_feat\"]])\n",
    "\n",
    "  # Features based\n",
    "  # shape = (cur_batch_size, num_feat, seq_len)\n",
    "  X_transposed = tf.transpose(a=X, perm=[0, 2, 1])\n",
    "\n",
    "  # shape = (cur_batch_size * num_feat, seq_len)\n",
    "  X_feat = tf.reshape(\n",
    "      tensor=X_transposed,\n",
    "      shape=[cur_batch_size * params[\"num_feat\"], params[\"seq_len\"]])\n",
    "\n",
    "  ##############################################################################\n",
    "\n",
    "  # Variables for calculating error distribution statistics\n",
    "  (pca_time_count_var,\n",
    "   pca_time_mean_var,\n",
    "   pca_time_cov_var,\n",
    "   pca_time_eigval_var,\n",
    "   pca_time_eigvec_var,\n",
    "   pca_time_k_pc_var,\n",
    "   pca_feat_count_var,\n",
    "   pca_feat_mean_var,\n",
    "   pca_feat_cov_var,\n",
    "   pca_feat_eigval_var,\n",
    "   pca_feat_eigvec_var,\n",
    "   pca_feat_k_pc_var) = create_both_pca_vars(\n",
    "      params[\"seq_len\"], params[\"num_feat\"])\n",
    "\n",
    "  # 3. Loss function, training/eval ops\n",
    "  if (mode == tf.estimator.ModeKeys.TRAIN and\n",
    "      params[\"training_mode\"] == \"reconstruction\"):\n",
    "    if not params[\"autotune_principal_components\"]:\n",
    "      with tf.variable_scope(name_or_scope=\"pca_vars\", reuse=tf.AUTO_REUSE):\n",
    "        # Check if batch is a singleton, very important for covariance math\n",
    "\n",
    "        # Time based\n",
    "        # shape = ()\n",
    "        singleton_condition = tf.equal(\n",
    "            x=cur_batch_size * params[\"seq_len\"], y=1)\n",
    "\n",
    "        pca_time_cov_var, pca_time_mean_var, pca_time_count_var = tf.cond(\n",
    "            pred=singleton_condition,\n",
    "            true_fn=lambda: singleton_batch_cov_variable_updating(\n",
    "                params[\"seq_len\"],\n",
    "                X_time,\n",
    "                pca_time_count_var,\n",
    "                pca_time_mean_var,\n",
    "                pca_time_cov_var),\n",
    "            false_fn=lambda: non_singleton_batch_cov_variable_updating(\n",
    "                cur_batch_size,\n",
    "                params[\"seq_len\"],\n",
    "                X_time,\n",
    "                pca_time_count_var,\n",
    "                pca_time_mean_var,\n",
    "                pca_time_cov_var))\n",
    "\n",
    "        # shape = (num_feat,) & (num_feat, num_feat)\n",
    "        pca_time_eigval_tensor, pca_time_eigvec_tensor = tf.linalg.eigh(\n",
    "            tensor=pca_time_cov_var)\n",
    "\n",
    "        if params[\"k_principal_components_time\"] is not None:\n",
    "          pca_time_k_pc = set_k_principal_components(\n",
    "              params[\"k_principal_components_time\"], pca_time_k_pc_var)\n",
    "        else:\n",
    "          pca_time_k_pc = tf.zeros(shape=(), dtype=tf.float64)\n",
    "\n",
    "        # Features based\n",
    "        # shape = ()\n",
    "        singleton_features_condition = tf.equal(\n",
    "            x=cur_batch_size * params[\"num_feat\"], y=1)\n",
    "\n",
    "        pca_feat_cov_var, pca_feat_mean_var, pca_feat_count_var = tf.cond(\n",
    "            pred=singleton_features_condition,\n",
    "            true_fn=lambda: singleton_batch_cov_variable_updating(\n",
    "                params[\"num_feat\"],\n",
    "                X_feat,\n",
    "                pca_feat_count_var, pca_feat_mean_var,\n",
    "                pca_feat_cov_var),\n",
    "            false_fn=lambda: non_singleton_batch_cov_variable_updating(\n",
    "                cur_batch_size,\n",
    "                params[\"num_feat\"],\n",
    "                X_feat,\n",
    "                pca_feat_count_var,\n",
    "                pca_feat_mean_var,\n",
    "                pca_feat_cov_var))\n",
    "\n",
    "        # shape = (seq_len,) & (seq_len, seq_len)\n",
    "        pca_feat_eigval_tensor, pca_feat_eigvec_tensor = tf.linalg.eigh(\n",
    "            tensor=pca_feat_cov_var)\n",
    "\n",
    "        if params[\"k_principal_components_feat\"] is not None:\n",
    "          pca_feat_k_pc = set_k_principal_components(\n",
    "              params[\"k_principal_components_feat\"], pca_feat_k_pc_var)\n",
    "        else:\n",
    "          pca_feat_k_pc = tf.zeros(shape=(), dtype=tf.float64)\n",
    "\n",
    "      # Lastly use control dependencies around loss to enforce the mahalanobis\n",
    "      # variables to be assigned, the control order matters, hence the separate\n",
    "      # contexts\n",
    "      with tf.control_dependencies(\n",
    "          control_inputs=[pca_time_cov_var, pca_feat_cov_var]):\n",
    "        with tf.control_dependencies(\n",
    "            control_inputs=[pca_time_mean_var, pca_feat_mean_var]):\n",
    "          with tf.control_dependencies(\n",
    "              control_inputs=[pca_time_count_var, pca_feat_count_var]):\n",
    "            with tf.control_dependencies(\n",
    "                control_inputs=[tf.assign(ref=pca_time_eigval_var,\n",
    "                                          value=pca_time_eigval_tensor),\n",
    "                                tf.assign(ref=pca_time_eigvec_var,\n",
    "                                          value=pca_time_eigvec_tensor),\n",
    "                                tf.assign(ref=pca_feat_eigval_var,\n",
    "                                          value=pca_feat_eigval_tensor),\n",
    "                                tf.assign(ref=pca_feat_eigvec_var,\n",
    "                                          value=pca_feat_eigvec_tensor),\n",
    "                                pca_time_k_pc,\n",
    "                                pca_feat_k_pc]):\n",
    "\n",
    "\n",
    "              loss = tf.reduce_sum(\n",
    "                  input_tensor=tf.zeros(\n",
    "                      shape=(), dtype=tf.float64) * dummy_var)\n",
    "\n",
    "              train_op = tf.contrib.layers.optimize_loss(\n",
    "                  loss=loss,\n",
    "                  global_step=tf.train.get_global_step(),\n",
    "                  learning_rate=params[\"learning_rate\"],\n",
    "                  optimizer=\"SGD\")\n",
    "\n",
    "              return loss, train_op, None, None, None, None\n",
    "    else:\n",
    "      # Time based\n",
    "      if params[\"k_principal_components_time\"] is None:\n",
    "        # shape = (cur_batch_size * seq_len, num_feat)\n",
    "        X_time_cen = X_time - pca_time_mean_var\n",
    "\n",
    "        # shape = (num_feat - 1,)\n",
    "        X_time_recon_mse = tf.map_fn(\n",
    "            fn=lambda x: pca_reconstruction_k_pc_mse(\n",
    "                X_time_cen, pca_time_eigvec_var, x),\n",
    "            elems=tf.range(start=1,\n",
    "                           limit=params[\"num_feat\"],\n",
    "                           dtype=tf.int64),\n",
    "            dtype=tf.float64)\n",
    "\n",
    "        pca_time_k_pc = find_best_k_principal_components(\n",
    "            X_time_recon_mse, pca_time_k_pc_var)\n",
    "      else:\n",
    "        pca_time_k_pc = set_k_principal_components(\n",
    "            params[\"k_principal_components_time\"], pca_time_k_pc_var)\n",
    "\n",
    "      if params[\"k_principal_components_feat\"] is None:\n",
    "        # Features based\n",
    "        # shape = (cur_batch_size * num_feat, seq_len)\n",
    "        X_feat_cen = X_feat - pca_feat_mean_var\n",
    "\n",
    "        # shape = (seq_len - 1,)\n",
    "        X_feat_recon_mse = tf.map_fn(\n",
    "            fn=lambda x: pca_reconstruction_k_pc_mse(\n",
    "                X_feat_cen, pca_feat_eigvec_var, x),\n",
    "            elems=tf.range(start=1,\n",
    "                           limit=params[\"seq_len\"],\n",
    "                           dtype=tf.int64),\n",
    "            dtype=tf.float64)\n",
    "\n",
    "        pca_feat_k_pc = find_best_k_principal_components(\n",
    "            X_feat_recon_mse, pca_feat_k_pc_var)\n",
    "      else:\n",
    "        pca_feat_k_pc = set_k_principal_components(\n",
    "            params[\"k_principal_components_feat\"], pca_feat_k_pc_var)\n",
    "\n",
    "      with tf.control_dependencies(\n",
    "          control_inputs=[pca_time_k_pc, pca_feat_k_pc]):\n",
    "        loss = tf.reduce_sum(\n",
    "            input_tensor=tf.zeros(\n",
    "                shape=(), dtype=tf.float64) * dummy_var)\n",
    "\n",
    "        train_op = tf.contrib.layers.optimize_loss(\n",
    "            loss=loss,\n",
    "            global_step=tf.train.get_global_step(),\n",
    "            learning_rate=params[\"learning_rate\"],\n",
    "            optimizer=\"SGD\")\n",
    "\n",
    "        return loss, train_op, None, None, None, None\n",
    "\n",
    "  else:\n",
    "    # Time based\n",
    "    # shape = (cur_batch_size * seq_len, num_feat)\n",
    "    X_time_cen = X_time - pca_time_mean_var\n",
    "\n",
    "    # shape = (cur_batch_size * seq_len, num_feat)\n",
    "    if params[\"k_principal_components_time\"] is None:\n",
    "      X_time_recon = pca_reconstruction_k_pc(\n",
    "          X_time_cen,\n",
    "          pca_time_eigvec_var,\n",
    "          pca_time_k_pc_var)\n",
    "    else:\n",
    "      X_time_recon = pca_reconstruction_k_pc(\n",
    "          X_time_cen,\n",
    "          pca_time_eigvec_var,\n",
    "          params[\"k_principal_components_time\"])\n",
    "\n",
    "    # Features based\n",
    "    # shape = (cur_batch_size * num_feat, seq_len)\n",
    "    X_feat_cen = X_feat - pca_feat_mean_var\n",
    "\n",
    "    # shape = (cur_batch_size * num_feat, seq_len)\n",
    "    if params[\"k_principal_components_feat\"] is None:\n",
    "      X_feat_recon = pca_reconstruction_k_pc(\n",
    "          X_feat_cen,\n",
    "          pca_feat_eigvec_var,\n",
    "          pca_feat_k_pc_var)\n",
    "    else:\n",
    "      X_feat_recon = pca_reconstruction_k_pc(\n",
    "          X_feat_cen,\n",
    "          pca_feat_eigvec_var,\n",
    "          params[\"k_principal_components_feat\"])\n",
    "\n",
    "    return None, None, X_time_cen, X_time_recon, X_feat_cen, X_feat_recon"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## reconstruction.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "def reconstruction_evaluation(X_time_orig, X_time_recon, training_mode):\n",
    "  \"\"\"Reconstruction loss on evaluation set.\n",
    "\n",
    "  Given time major original and reconstructed features data and the training\n",
    "  mode, return loss and eval_metrics_ops.\n",
    "\n",
    "  Args:\n",
    "    X_time_orig: Time major original features data.\n",
    "    X_time_recon: Time major reconstructed features data.\n",
    "    training_mode: Current training mode.\n",
    "\n",
    "  Returns:\n",
    "    loss: Scalar reconstruction loss.\n",
    "    eval_metric_ops: Evaluation metrics of reconstruction.\n",
    "  \"\"\"\n",
    "  loss = tf.losses.mean_squared_error(\n",
    "      labels=X_time_orig, predictions=X_time_recon)\n",
    "\n",
    "  eval_metric_ops = None\n",
    "\n",
    "  if training_mode == \"reconstruction\":\n",
    "    # Reconstruction eval metrics\n",
    "    eval_metric_ops = {\n",
    "        \"rmse\": tf.metrics.root_mean_squared_error(\n",
    "            labels=X_time_orig, predictions=X_time_recon),\n",
    "        \"mae\": tf.metrics.mean_absolute_error(\n",
    "            labels=X_time_orig, predictions=X_time_recon)\n",
    "    }\n",
    "\n",
    "  return loss, eval_metric_ops"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## error_distribution_vars.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_mahalanobis_dist_vars(var_name, size):\n",
    "  \"\"\"Creates mahalanobis distance variables.\n",
    "\n",
    "  Given variable name and size, create and return mahalanobis distance variables\n",
    "  for count, mean, covariance, and inverse covariance.\n",
    "\n",
    "  Args:\n",
    "    var_name: String denoting which set of variables to create. Values are\n",
    "      \"time\" and \"feat\".\n",
    "    size: The size of the variable, either sequence length or number of\n",
    "      features.\n",
    "\n",
    "  Returns:\n",
    "    Mahalanobis distance variables for count, mean, covariance, and inverse\n",
    "    covariance.\n",
    "  \"\"\"\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=\"mahalanobis_dist_vars\", reuse=tf.AUTO_REUSE):\n",
    "    count_var = tf.get_variable(\n",
    "        name=\"abs_err_count_{0}_var\".format(var_name),\n",
    "        dtype=tf.int64,\n",
    "        initializer=tf.zeros(shape=[], dtype=tf.int64),\n",
    "        trainable=False)\n",
    "\n",
    "    mean_var = tf.get_variable(\n",
    "        name=\"abs_err_mean_{0}_var\".format(var_name),\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(shape=[size], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "    cov_var = tf.get_variable(\n",
    "        name=\"abs_err_cov_{0}_var\".format(var_name),\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(shape=[size, size], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "    inv_cov_var = tf.get_variable(\n",
    "        name=\"abs_err_inv_cov_{0}_var\".format(var_name),\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(shape=[size, size], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "  return count_var, mean_var, cov_var, inv_cov_var\n",
    "\n",
    "\n",
    "def create_both_mahalanobis_dist_vars(seq_len, num_feat):\n",
    "  \"\"\"Creates both time & feature major mahalanobis distance variables.\n",
    "\n",
    "  Given dimensions of inputs, create and return mahalanobis distance variables\n",
    "  for count, mean, covariance, and inverse covariance for both time and\n",
    "  feature major representations.\n",
    "\n",
    "  Args:\n",
    "    seq_len: Number of timesteps in sequence.\n",
    "    num_feat: Number of features.\n",
    "\n",
    "  Returns:\n",
    "    Mahalanobis distance variables for count, mean, covariance, and inverse\n",
    "    covariance for both time and feature major representations.\n",
    "  \"\"\"\n",
    "  # Time based\n",
    "  (abs_err_count_time_var,\n",
    "   abs_err_mean_time_var,\n",
    "   abs_err_cov_time_var,\n",
    "   abs_err_inv_cov_time_var) = create_mahalanobis_dist_vars(\n",
    "       var_name=\"time\", size=num_feat)\n",
    "\n",
    "  # Features based\n",
    "  (abs_err_count_feat_var,\n",
    "   abs_err_mean_feat_var,\n",
    "   abs_err_cov_feat_var,\n",
    "   abs_err_inv_cov_feat_var) = create_mahalanobis_dist_vars(\n",
    "       var_name=\"feat\", size=seq_len)\n",
    "\n",
    "  return (abs_err_count_time_var,\n",
    "          abs_err_mean_time_var,\n",
    "          abs_err_cov_time_var,\n",
    "          abs_err_inv_cov_time_var,\n",
    "          abs_err_count_feat_var,\n",
    "          abs_err_mean_feat_var,\n",
    "          abs_err_cov_feat_var,\n",
    "          abs_err_inv_cov_feat_var)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## calculate_error_distribution_statistics.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Running covariance updating functions for mahalanobis distance variables\n",
    "def update_record_count(count_a, count_b):\n",
    "  \"\"\"Updates the running number of records processed.\n",
    "\n",
    "  Given previous running total and current batch size, return new running total.\n",
    "\n",
    "  Args:\n",
    "    count_a: tf.int64 scalar tensor of previous running total of records.\n",
    "    count_b: tf.int64 scalar tensor of current batch size.\n",
    "\n",
    "  Returns:\n",
    "    A tf.int64 scalar tensor of new running total of records.\n",
    "  \"\"\"\n",
    "  return count_a + count_b\n",
    "\n",
    "\n",
    "# Incremental covariance updating functions for mahalanobis distance variables\n",
    "\n",
    "\n",
    "def update_mean_incremental(count_a, mean_a, value_b):\n",
    "  \"\"\"Updates the running mean vector incrementally.\n",
    "\n",
    "  Given previous running total, running column means, and single example's\n",
    "  column values, return new running column means.\n",
    "\n",
    "  Args:\n",
    "    count_a: tf.int64 scalar tensor of previous running total of records.\n",
    "    mean_a: tf.float64 vector tensor of previous running column means.\n",
    "    value_b: tf.float64 vector tensor of single example's column values.\n",
    "\n",
    "  Returns:\n",
    "    A tf.float64 vector tensor of new running column means.\n",
    "  \"\"\"\n",
    "  umean_a = mean_a * tf.cast(x=count_a, dtype=tf.float64)\n",
    "  mean_ab_num = umean_a + tf.squeeze(input=value_b, axis=0)\n",
    "  mean_ab = mean_ab_num / tf.cast(x=count_a + 1, dtype=tf.float64)\n",
    "\n",
    "  return mean_ab\n",
    "\n",
    "\n",
    "# This function updates the covariance matrix incrementally\n",
    "def update_cov_incremental(\n",
    "    count_a, mean_a, cov_a, value_b, mean_ab, sample_cov):\n",
    "  \"\"\"Updates the running covariance matrix incrementally.\n",
    "\n",
    "  Given previous running total, running column means, running covariance matrix,\n",
    "  single example's column values, new running column means, and whether to use\n",
    "  sample covariance or not, return new running covariance matrix.\n",
    "\n",
    "  Args:\n",
    "    count_a: tf.int64 scalar tensor of previous running total of records.\n",
    "    mean_a: tf.float64 vector tensor of previous running column means.\n",
    "    cov_a: tf.float64 matrix tensor of previous running covariance matrix.\n",
    "    value_b: tf.float64 vector tensor of single example's column values.\n",
    "    mean_ab: tf.float64 vector tensor of new running column means.\n",
    "    sample_cov: Bool flag on whether sample or population covariance is used.\n",
    "\n",
    "  Returns:\n",
    "    A tf.float64 matrix tensor of new covariance matrix.\n",
    "  \"\"\"\n",
    "  print(\"value_b = \\n{}\".format(value_b))\n",
    "  print(\"mean_a = \\n{}\".format(mean_a))\n",
    "  print(\"mean_ab = \\n{}\".format(mean_ab))\n",
    "  mean_diff = tf.matmul(\n",
    "      a=value_b - mean_a, b=value_b - mean_ab, transpose_a=True)\n",
    "  if sample_cov:\n",
    "    ucov_a = cov_a * tf.cast(x=count_a - 1, dtype=tf.float64)\n",
    "    cov_ab = (ucov_a + mean_diff) / tf.cast(x=count_a, dtype=tf.float64)\n",
    "  else:\n",
    "    ucov_a = cov_a * tf.cast(x=count_a, dtype=tf.float64)\n",
    "    cov_ab = (ucov_a + mean_diff) / tf.cast(x=count_a + 1, dtype=tf.float64)\n",
    "\n",
    "  return cov_ab\n",
    "\n",
    "\n",
    "def singleton_batch_cov_variable_updating(\n",
    "    inner_size, X, count_variable, mean_variable, cov_variable):\n",
    "  \"\"\"Updates mahalanobis variables incrementally when number_of_rows equals 1.\n",
    "\n",
    "  Given the inner size of the matrix, the data vector X, the variable tracking\n",
    "  running record counts, the variable tracking running column means, and the\n",
    "  variable tracking running covariance matrix, returns updated running\n",
    "  covariance matrix, running column means, and running record count variables.\n",
    "\n",
    "  Args:\n",
    "    inner_size: Inner size of matrix X.\n",
    "    X: tf.float64 matrix tensor of input data.\n",
    "    count_variable: tf.int64 scalar variable tracking running record counts.\n",
    "    mean_variable: tf.float64 vector variable tracking running column means.\n",
    "    cov_variable: tf.float64 matrix variable tracking running covariance matrix.\n",
    "\n",
    "  Returns:\n",
    "    Updated running covariance matrix, running column means, and running record\n",
    "      count variables.\n",
    "  \"\"\"\n",
    "  # Calculate new combined mean for incremental covariance matrix calculation\n",
    "  # time_shape = (num_feat,), features_shape = (seq_len,)\n",
    "  mean_ab = update_mean_incremental(\n",
    "      count_a=count_variable, mean_a=mean_variable, value_b=X)\n",
    "\n",
    "  # Update running variables from single example\n",
    "  # time_shape = (), features_shape = ()\n",
    "  count_tensor = update_record_count(count_a=count_variable, count_b=1)\n",
    "\n",
    "  # time_shape = (num_feat,), features_shape = (seq_len,)\n",
    "  mean_tensor = mean_ab\n",
    "\n",
    "  # Check if inner dimension is greater than 1 to calculate covariance matrix\n",
    "  if inner_size == 1:\n",
    "    cov_tensor = tf.zeros_like(tensor=cov_variable, dtype=tf.float64)\n",
    "  else:\n",
    "    # time_shape = (num_feat, num_feat)\n",
    "    # features_shape = (seq_len, seq_len)\n",
    "    cov_tensor = update_cov_incremental(\n",
    "        count_a=count_variable,\n",
    "        mean_a=mean_variable,\n",
    "        cov_a=cov_variable,\n",
    "        value_b=X,\n",
    "        mean_ab=mean_ab,\n",
    "        sample_cov=True)\n",
    "\n",
    "  # Assign values to variables, use control dependencies around return to\n",
    "  # enforce the mahalanobis variables to be assigned, the control order matters,\n",
    "  # hence the separate contexts.\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[tf.assign(ref=cov_variable, value=cov_tensor)]):\n",
    "    with tf.control_dependencies(\n",
    "        control_inputs=[tf.assign(ref=mean_variable, value=mean_tensor)]):\n",
    "      with tf.control_dependencies(\n",
    "          control_inputs=[tf.assign(ref=count_variable, value=count_tensor)]):\n",
    "\n",
    "        return (tf.identity(input=cov_variable),\n",
    "                tf.identity(input=mean_variable),\n",
    "                tf.identity(input=count_variable))\n",
    "\n",
    "\n",
    "def singleton_batch_var_variable_updating(\n",
    "    inner_size, x, count_variable, mean_variable, var_variable):\n",
    "  \"\"\"Updates mahalanobis thresh vars incrementally when number_of_rows equals 1.\n",
    "\n",
    "  Given the inner size of the matrix, the data scalar x, the variable tracking\n",
    "  running record counts, the variable tracking the running mean, and the\n",
    "  variable tracking the running variance, returns updated running variance,\n",
    "  running mean, and running record count variables.\n",
    "\n",
    "  Args:\n",
    "    inner_size: Inner size of matrix X.\n",
    "    x: tf.float64 scalar tensor of input data.\n",
    "    count_variable: tf.int64 scalar variable tracking running record counts.\n",
    "    mean_variable: tf.float64 scalar variable tracking running mean.\n",
    "    var_variable: tf.float64 scalar variable tracking running variance.\n",
    "\n",
    "  Returns:\n",
    "    Updated running variance, running mean, and running record count variables.\n",
    "  \"\"\"\n",
    "  # Calculate new combined mean for incremental covariance matrix calculation\n",
    "  # time_shape = (), features_shape = ()\n",
    "  mean_ab = update_mean_incremental(\n",
    "      count_a=count_variable, mean_a=mean_variable, value_b=x)\n",
    "\n",
    "  # Update running variables from single example\n",
    "  # time_shape = (), features_shape = ()\n",
    "  count_tensor = update_record_count(count_a=count_variable, count_b=1)\n",
    "\n",
    "  # time_shape = (), features_shape = ()\n",
    "  mean_tensor = mean_ab\n",
    "\n",
    "  # Check if inner dimension is greater than 1 to calculate covariance matrix\n",
    "  if inner_size == 1:\n",
    "    var_tensor = tf.zeros_like(tensor=var_variable, dtype=tf.float64)\n",
    "  else:\n",
    "    # time_shape = (), features_shape = ()\n",
    "    var_tensor = update_cov_incremental(\n",
    "        count_a=count_variable,\n",
    "        mean_a=tf.reshape(tensor=mean_variable, shape=[1]),\n",
    "        cov_a=tf.reshape(tensor=var_variable, shape=[1, 1]),\n",
    "        value_b=tf.reshape(tensor=x, shape=[1, 1]),\n",
    "        mean_ab=tf.reshape(tensor=mean_ab, shape=[1]),\n",
    "        sample_cov=True)\n",
    "\n",
    "    var_tensor = tf.squeeze(input=var_tensor)\n",
    "\n",
    "  # Assign values to variables, use control dependencies around return to\n",
    "  # enforce the mahalanobis variables to be assigned, the control order matters,\n",
    "  # hence the separate contexts.\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[tf.assign(ref=var_variable, value=var_tensor)]):\n",
    "    with tf.control_dependencies(\n",
    "        control_inputs=[tf.assign(ref=mean_variable, value=mean_tensor)]):\n",
    "      with tf.control_dependencies(\n",
    "          control_inputs=[tf.assign(ref=count_variable, value=count_tensor)]):\n",
    "\n",
    "        return (tf.identity(input=var_variable),\n",
    "                tf.identity(input=mean_variable),\n",
    "                tf.identity(input=count_variable))\n",
    "\n",
    "\n",
    "# Batch covariance updating functions for mahalanobis distance variables\n",
    "\n",
    "\n",
    "def update_mean_batch(count_a, mean_a, count_b, mean_b):\n",
    "  \"\"\"Updates the running mean vector with a batch of data.\n",
    "\n",
    "  Given previous running total, running column means, current batch size, and\n",
    "  batch's column means, return new running column means.\n",
    "\n",
    "  Args:\n",
    "    count_a: tf.int64 scalar tensor of previous running total of records.\n",
    "    mean_a: tf.float64 vector tensor of previous running column means.\n",
    "    count_b: tf.int64 scalar tensor of current batch size.\n",
    "    mean_b: tf.float64 vector tensor of batch's column means.\n",
    "\n",
    "  Returns:\n",
    "    A tf.float64 vector tensor of new running column means.\n",
    "  \"\"\"\n",
    "  sum_a = mean_a * tf.cast(x=count_a, dtype=tf.float64)\n",
    "  sum_b = mean_b * tf.cast(x=count_b, dtype=tf.float64)\n",
    "  mean_ab = (sum_a + sum_b) / tf.cast(x=count_a + count_b, dtype=tf.float64)\n",
    "\n",
    "  return mean_ab\n",
    "\n",
    "\n",
    "def update_cov_batch(\n",
    "    count_a, mean_a, cov_a, count_b, mean_b, cov_b, sample_cov):\n",
    "  \"\"\"Updates the running covariance matrix with batch of data.\n",
    "\n",
    "  Given previous running total, running column means, running covariance matrix,\n",
    "  current batch size, batch's column means, batch's covariance matrix, and\n",
    "  whether to use sample covariance or not, return new running covariance matrix.\n",
    "\n",
    "  Args:\n",
    "    count_a: tf.int64 scalar tensor of previous running total of records.\n",
    "    mean_a: tf.float64 vector tensor of previous running column means.\n",
    "    cov_a: tf.float64 matrix tensor of previous running covariance matrix.\n",
    "    count_b: tf.int64 scalar tensor of current batch size.\n",
    "    mean_b: tf.float64 vector tensor of batch's column means.\n",
    "    cov_b: tf.float64 matrix tensor of batch's covariance matrix.\n",
    "    sample_cov: Bool flag on whether sample or population covariance is used.\n",
    "\n",
    "  Returns:\n",
    "    A tf.float64 matrix tensor of new running covariance matrix.\n",
    "  \"\"\"\n",
    "  mean_diff = tf.expand_dims(input=mean_a - mean_b, axis=0)\n",
    "\n",
    "  if sample_cov:\n",
    "    ucov_a = cov_a * tf.cast(x=count_a - 1, dtype=tf.float64)\n",
    "    ucov_b = cov_b * tf.cast(x=count_b - 1, dtype=tf.float64)\n",
    "    den = tf.cast(x=count_a + count_b - 1, dtype=tf.float64)\n",
    "  else:\n",
    "    ucov_a = cov_a * tf.cast(x=count_a, dtype=tf.float64)\n",
    "    ucov_b = cov_b * tf.cast(x=count_b, dtype=tf.float64)\n",
    "    den = tf.cast(x=count_a + count_b, dtype=tf.float64)\n",
    "\n",
    "  mean_diff = tf.matmul(a=mean_diff, b=mean_diff, transpose_a=True)\n",
    "  mean_scaling_num = tf.cast(x=count_a * count_b, dtype=tf.float64)\n",
    "  mean_scaling_den = tf.cast(x=count_a + count_b, dtype=tf.float64)\n",
    "  mean_scaling = mean_scaling_num / mean_scaling_den\n",
    "  cov_ab = (ucov_a + ucov_b + mean_diff * mean_scaling) / den\n",
    "\n",
    "  return cov_ab\n",
    "\n",
    "\n",
    "def non_singleton_batch_cov_variable_updating(\n",
    "    cur_batch_size, inner_size, X, count_variable, mean_variable, cov_variable):\n",
    "  \"\"\"Updates mahalanobis variables when number_of_rows does NOT equal 1.\n",
    "\n",
    "  Given the current batch size, inner size of the matrix, the data matrix X,\n",
    "  the variable tracking running record counts, the variable tracking running\n",
    "  column means, and the variable tracking running covariance matrix, returns\n",
    "  updated running covariance matrix, running column means, and running record\n",
    "  count variables.\n",
    "\n",
    "  Args:\n",
    "    cur_batch_size: Number of examples in current batch (could be partial).\n",
    "    inner_size: Inner size of matrix X.\n",
    "    X: tf.float64 matrix tensor of input data.\n",
    "    count_variable: tf.int64 scalar variable tracking running record counts.\n",
    "    mean_variable: tf.float64 vector variable tracking running column means.\n",
    "    cov_variable: tf.float64 matrix variable tracking running covariance matrix.\n",
    "\n",
    "  Returns:\n",
    "    Updated running covariance matrix, running column means, and running record\n",
    "      count variables.\n",
    "  \"\"\"\n",
    "  # Find statistics of batch\n",
    "  number_of_rows = cur_batch_size * inner_size\n",
    "\n",
    "  # time_shape = (num_feat,), features_shape = (seq_len,)\n",
    "  X_mean = tf.reduce_mean(input_tensor=X, axis=0)\n",
    "\n",
    "  # time_shape = (cur_batch_size * seq_len, num_feat)\n",
    "  # features_shape = (cur_batch_size * num_feat, seq_len)\n",
    "  X_centered = X - X_mean\n",
    "\n",
    "  if inner_size > 1:\n",
    "    # time_shape = (num_feat, num_feat)\n",
    "    # features_shape = (seq_len, seq_len)\n",
    "    X_cov = tf.matmul(\n",
    "        a=X_centered,\n",
    "        b=X_centered,\n",
    "        transpose_a=True) / tf.cast(x=number_of_rows - 1, dtype=tf.float64)\n",
    "\n",
    "  # Update running variables from batch statistics\n",
    "  # time_shape = (), features_shape = ()\n",
    "  count_tensor = update_record_count(\n",
    "      count_a=count_variable, count_b=number_of_rows)\n",
    "\n",
    "  # time_shape = (num_feat,), features_shape = (seq_len,)\n",
    "  mean_tensor = update_mean_batch(\n",
    "      count_a=count_variable,\n",
    "      mean_a=mean_variable,\n",
    "      count_b=number_of_rows,\n",
    "      mean_b=X_mean)\n",
    "\n",
    "  # Check if inner dimension is greater than 1 to calculate covariance matrix\n",
    "  if inner_size == 1:\n",
    "    cov_tensor = tf.zeros_like(tensor=cov_variable, dtype=tf.float64)\n",
    "  else:\n",
    "    # time_shape = (num_feat, num_feat)\n",
    "    # features_shape = (seq_len, seq_len)\n",
    "    cov_tensor = update_cov_batch(\n",
    "        count_a=count_variable,\n",
    "        mean_a=mean_variable,\n",
    "        cov_a=cov_variable,\n",
    "        count_b=number_of_rows,\n",
    "        mean_b=X_mean,\n",
    "        cov_b=X_cov,\n",
    "        sample_cov=True)\n",
    "\n",
    "  # Assign values to variables, use control dependencies around return to\n",
    "  # enforce the mahalanobis variables to be assigned, the control order matters,\n",
    "  # hence the separate contexts.\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[tf.assign(ref=cov_variable, value=cov_tensor)]):\n",
    "    with tf.control_dependencies(\n",
    "        control_inputs=[tf.assign(ref=mean_variable, value=mean_tensor)]):\n",
    "      with tf.control_dependencies(\n",
    "          control_inputs=[tf.assign(ref=count_variable, value=count_tensor)]):\n",
    "\n",
    "        return (tf.identity(input=cov_variable),\n",
    "                tf.identity(input=mean_variable),\n",
    "                tf.identity(input=count_variable))\n",
    "\n",
    "\n",
    "def non_singleton_batch_var_variable_updating(\n",
    "    cur_batch_size, inner_size, x, count_variable, mean_variable, var_variable):\n",
    "  \"\"\"Updates mahalanobis thresh variables when number_of_rows does NOT equal 1.\n",
    "\n",
    "  Given the current batch size, inner size of the matrix, the data vector x,\n",
    "  the variable tracking the running record count, the variable tracking the\n",
    "  running mean, and the variable tracking the running variance, returns\n",
    "  updated running variance, running mean, and running record count variables.\n",
    "\n",
    "  Args:\n",
    "    cur_batch_size: Number of examples in current batch (could be partial).\n",
    "    inner_size: Inner size of matrix X.\n",
    "    x: tf.float64 vector tensor of mahalanobis distance.\n",
    "    count_variable: tf.int64 scalar variable tracking running record count.\n",
    "    mean_variable: tf.float64 scalar variable tracking running mean.\n",
    "    var_variable: tf.float64 scalar variable tracking running variance.\n",
    "\n",
    "  Returns:\n",
    "    Updated running variance, running mean, and running record count variables.\n",
    "  \"\"\"\n",
    "  # Find statistics of batch\n",
    "  number_of_rows = cur_batch_size * inner_size\n",
    "\n",
    "  # time_shape = (), features_shape = ()\n",
    "  x_mean = tf.reduce_mean(input_tensor=x)\n",
    "\n",
    "  # time_shape = (cur_batch_size * seq_len,)\n",
    "  # features_shape = (cur_batch_size * num_feat,)\n",
    "  x_centered = x - x_mean\n",
    "\n",
    "  if inner_size > 1:\n",
    "    # time_shape = (), features_shape = ()\n",
    "    x_var = tf.reduce_sum(input_tensor=tf.square(x=x_centered))\n",
    "    x_var /= tf.cast(x=number_of_rows - 1, dtype=tf.float64)\n",
    "\n",
    "  # Update running variables from batch statistics\n",
    "  # time_shape = (), features_shape = ()\n",
    "  count_tensor = update_record_count(\n",
    "      count_a=count_variable, count_b=number_of_rows)\n",
    "\n",
    "  # time_shape = (), features_shape = ()\n",
    "  mean_tensor = update_mean_batch(\n",
    "      count_a=count_variable,\n",
    "      mean_a=mean_variable,\n",
    "      count_b=number_of_rows,\n",
    "      mean_b=x_mean)\n",
    "\n",
    "  # Check if inner dimension is greater than 1 to calculate covariance matrix\n",
    "  if inner_size == 1:\n",
    "    var_tensor = tf.zeros_like(tensor=var_variable, dtype=tf.float64)\n",
    "  else:\n",
    "    # time_shape = (num_feat, num_feat)\n",
    "    # features_shape = (seq_len, seq_len)\n",
    "    var_tensor = update_cov_batch(\n",
    "        count_a=count_variable,\n",
    "        mean_a=mean_variable,\n",
    "        cov_a=var_variable,\n",
    "        count_b=number_of_rows,\n",
    "        mean_b=tf.expand_dims(input=x_mean, axis=0),\n",
    "        cov_b=tf.reshape(tensor=x_var, shape=[1, 1]),\n",
    "        sample_cov=True)\n",
    "\n",
    "    var_tensor = tf.squeeze(input=var_tensor)\n",
    "\n",
    "  # Assign values to variables, use control dependencies around return to\n",
    "  # enforce the mahalanobis thresh variables to be assigned, the control order\n",
    "  # matters, hence the separate contexts.\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[tf.assign(ref=var_variable, value=var_tensor)]):\n",
    "    with tf.control_dependencies(\n",
    "        control_inputs=[tf.assign(ref=mean_variable, value=mean_tensor)]):\n",
    "      with tf.control_dependencies(\n",
    "          control_inputs=[tf.assign(ref=count_variable, value=count_tensor)]):\n",
    "\n",
    "        return (tf.identity(input=var_variable),\n",
    "                tf.identity(input=mean_variable),\n",
    "                tf.identity(input=count_variable))\n",
    "\n",
    "\n",
    "def mahalanobis_dist(err_vec, mean_vec, inv_cov, final_shape):\n",
    "  \"\"\"Calculates mahalanobis distance from MLE.\n",
    "\n",
    "  Given reconstruction error vector, mean reconstruction error vector, inverse\n",
    "  covariance of reconstruction error, and mahalanobis distance tensor's final\n",
    "  shape, return mahalanobis distance.\n",
    "\n",
    "  Args:\n",
    "    err_vec: tf.float64 matrix tensor of reconstruction errors.\n",
    "    mean_vec: tf.float64 vector variable tracking running column means of\n",
    "      reconstruction errors.\n",
    "    inv_cov: tf.float64 matrix variable tracking running covariance matrix of\n",
    "      reconstruction errors.\n",
    "    final_shape: Final shape of mahalanobis distance tensor.\n",
    "\n",
    "  Returns:\n",
    "    tf.float64 matrix tensor of mahalanobis distance.\n",
    "  \"\"\"\n",
    "  # time_shape = (cur_batch_size * seq_len, num_feat)\n",
    "  # features_shape = (cur_batch_size * num_feat, seq_len)\n",
    "  err_vec_cen = err_vec - mean_vec\n",
    "\n",
    "  # time_shape = (num_feat, cur_batch_size * seq_len)\n",
    "  # features_shape = (seq_len, cur_batch_size * num_feat)\n",
    "  mahalanobis_right_product = tf.matmul(\n",
    "      a=inv_cov, b=err_vec_cen, transpose_b=True)\n",
    "\n",
    "  # time_shape = (cur_batch_size * seq_len, cur_batch_size * seq_len)\n",
    "  # features_shape = (cur_batch_size * num_feat, cur_batch_size * num_feat)\n",
    "  mahalanobis_dist_vectorized = tf.matmul(\n",
    "      a=err_vec_cen, b=mahalanobis_right_product)\n",
    "\n",
    "  # time_shape = (cur_batch_size * seq_len,)\n",
    "  # features_shape = (cur_batch_size * num_feat,)\n",
    "  mahalanobis_dist_flat = tf.diag_part(input=mahalanobis_dist_vectorized)\n",
    "\n",
    "  # time_shape = (cur_batch_size, seq_len)\n",
    "  # features_shape = (cur_batch_size, num_feat)\n",
    "  mahalanobis_dist_final_shaped = tf.reshape(\n",
    "      tensor=mahalanobis_dist_flat, shape=[-1, final_shape])\n",
    "\n",
    "  # time_shape = (cur_batch_size, seq_len)\n",
    "  # features_shape = (cur_batch_size, num_feat)\n",
    "  mahalanobis_dist_final_shaped_sqrt = tf.sqrt(x=mahalanobis_dist_final_shaped)\n",
    "\n",
    "  return mahalanobis_dist_final_shaped_sqrt\n",
    "\n",
    "\n",
    "def calculate_error_distribution_statistics_training(\n",
    "    cur_batch_size,\n",
    "    X_time_abs_recon_err,\n",
    "    abs_err_count_time_var,\n",
    "    abs_err_mean_time_var,\n",
    "    abs_err_cov_time_var,\n",
    "    abs_err_inv_cov_time_var,\n",
    "    X_feat_abs_recon_err,\n",
    "    abs_err_count_feat_var,\n",
    "    abs_err_mean_feat_var,\n",
    "    abs_err_cov_feat_var,\n",
    "    abs_err_inv_cov_feat_var,\n",
    "    params,\n",
    "    dummy_var):\n",
    "  \"\"\"Calculates error distribution statistics during training mode.\n",
    "\n",
    "  Given dimensions of inputs, reconstructed inputs' absolute errors, and\n",
    "  variables tracking counts, means, and covariances of error distribution,\n",
    "  returns loss and train_op.\n",
    "\n",
    "  Args:\n",
    "    cur_batch_size: Current batch size, could be partially filled.\n",
    "    X_time_abs_recon_err: Time major reconstructed input data's absolute\n",
    "      reconstruction error.\n",
    "    abs_err_count_time_var: Time major running count of number of records.\n",
    "    abs_err_mean_time_var: Time major running column means of absolute error.\n",
    "    abs_err_cov_time_var: Time major running covariance matrix of absolute\n",
    "      error.\n",
    "    abs_err_inv_cov_time_var: Time major running inverse covariance matrix of\n",
    "    absolute error.\n",
    "    X_feat_abs_recon_err: Feature major reconstructed input data's absolute\n",
    "      reconstruction error.\n",
    "    abs_err_count_feat_var: Feature major running count of number of records.\n",
    "    abs_err_mean_feat_var: Feature major running column means of absolute error.\n",
    "    abs_err_cov_feat_var: Feature major running covariance matrix of absolute\n",
    "      error.\n",
    "    abs_err_inv_cov_feat_var: Feature major running inverse covariance matrix of\n",
    "    absolute error.\n",
    "    params: Dictionary of parameters.\n",
    "    dummy_var: Dummy variable used to allow training mode to happen since it\n",
    "      requires a gradient to tie back to the graph dependency.\n",
    "\n",
    "  Returns:\n",
    "    loss: The scalar loss to tie our updates back to Estimator graph.\n",
    "    train_op: The train operation to tie our updates back to Estimator graph.\n",
    "  \"\"\"\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=\"mahalanobis_dist_vars\", reuse=tf.AUTO_REUSE):\n",
    "    # Time based\n",
    "    singleton_time_condition = tf.equal(\n",
    "        x=cur_batch_size * params[\"seq_len\"], y=1)\n",
    "\n",
    "    cov_time_var, mean_time_var, count_time_var = tf.cond(\n",
    "        pred=singleton_time_condition,\n",
    "        true_fn=lambda: singleton_batch_cov_variable_updating(\n",
    "            params[\"seq_len\"],\n",
    "            X_time_abs_recon_err,\n",
    "            abs_err_count_time_var,\n",
    "            abs_err_mean_time_var,\n",
    "            abs_err_cov_time_var),\n",
    "        false_fn=lambda: non_singleton_batch_cov_variable_updating(\n",
    "            cur_batch_size,\n",
    "            params[\"seq_len\"],\n",
    "            X_time_abs_recon_err,\n",
    "            abs_err_count_time_var,\n",
    "            abs_err_mean_time_var,\n",
    "            abs_err_cov_time_var))\n",
    "\n",
    "    # Features based\n",
    "    singleton_feat_condition = tf.equal(\n",
    "        x=cur_batch_size * params[\"num_feat\"], y=1)\n",
    "\n",
    "    cov_feat_var, mean_feat_var, count_feat_var = tf.cond(\n",
    "        pred=singleton_feat_condition,\n",
    "        true_fn=lambda: singleton_batch_cov_variable_updating(\n",
    "            params[\"num_feat\"],\n",
    "            X_feat_abs_recon_err,\n",
    "            abs_err_count_feat_var,\n",
    "            abs_err_mean_feat_var,\n",
    "            abs_err_cov_feat_var),\n",
    "        false_fn=lambda: non_singleton_batch_cov_variable_updating(\n",
    "            cur_batch_size,\n",
    "            params[\"num_feat\"],\n",
    "            X_feat_abs_recon_err,\n",
    "            abs_err_count_feat_var,\n",
    "            abs_err_mean_feat_var,\n",
    "            abs_err_cov_feat_var))\n",
    "\n",
    "  # Lastly use control dependencies around loss to enforce the mahalanobis\n",
    "  # variables to be assigned, the control order matters, hence the separate\n",
    "  # contexts\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[cov_time_var, cov_feat_var]):\n",
    "    with tf.control_dependencies(\n",
    "        control_inputs=[mean_time_var, mean_feat_var]):\n",
    "      with tf.control_dependencies(\n",
    "          control_inputs=[count_time_var, count_feat_var]):\n",
    "        # Time based\n",
    "        # shape = (num_feat, num_feat)\n",
    "        abs_err_inv_cov_time_tensor = \\\n",
    "          tf.matrix_inverse(input=cov_time_var + \\\n",
    "            tf.eye(num_rows=tf.shape(input=cov_time_var)[0],\n",
    "                   dtype=tf.float64) * params[\"eps\"])\n",
    "        # Features based\n",
    "        # shape = (seq_len, seq_len)\n",
    "        abs_err_inv_cov_feat_tensor = \\\n",
    "          tf.matrix_inverse(input=cov_feat_var + \\\n",
    "            tf.eye(num_rows=tf.shape(input=cov_feat_var)[0],\n",
    "                   dtype=tf.float64) * params[\"eps\"])\n",
    "\n",
    "        with tf.control_dependencies(\n",
    "            control_inputs=[tf.assign(ref=abs_err_inv_cov_time_var,\n",
    "                                      value=abs_err_inv_cov_time_tensor),\n",
    "                            tf.assign(ref=abs_err_inv_cov_feat_var,\n",
    "                                      value=abs_err_inv_cov_feat_tensor)]):\n",
    "          loss = tf.reduce_sum(\n",
    "              input_tensor=tf.zeros(shape=(), dtype=tf.float64) * dummy_var)\n",
    "\n",
    "          train_op = tf.contrib.layers.optimize_loss(\n",
    "              loss=loss,\n",
    "              global_step=tf.train.get_global_step(),\n",
    "              learning_rate=params[\"learning_rate\"],\n",
    "              optimizer=\"SGD\")\n",
    "\n",
    "  return loss, train_op"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## tune_anomaly_threshold_vars.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_confusion_matrix_thresh_vars(scope, var_name, size):\n",
    "  \"\"\"Creates confusion matrix threshold variables.\n",
    "\n",
    "  Given variable scope, name, and size, create and return confusion matrix\n",
    "  threshold variables for true positives, false negatives, false positives,\n",
    "  true negatives.\n",
    "\n",
    "  Args:\n",
    "    scope: String of variable scope name.\n",
    "    var_name: String denoting which set of variables to create. Values are\n",
    "      \"time\" and \"feat\".\n",
    "    size: The size of the variable, number of time/feature thresholds.\n",
    "\n",
    "  Returns:\n",
    "    Confusion matrix threshold variables for true positives, false negatives,\n",
    "    false positives, true negatives.\n",
    "  \"\"\"\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=scope, reuse=tf.AUTO_REUSE):\n",
    "    tp_thresh_var = tf.get_variable(\n",
    "        name=\"tp_thresh_{0}_var\".format(var_name),\n",
    "        dtype=tf.int64,\n",
    "        initializer=tf.zeros(\n",
    "            shape=size, dtype=tf.int64),\n",
    "        trainable=False)\n",
    "\n",
    "    fn_thresh_var = tf.get_variable(\n",
    "        name=\"fn_thresh_{0}_var\".format(var_name),\n",
    "        dtype=tf.int64,\n",
    "        initializer=tf.zeros(\n",
    "            shape=size, dtype=tf.int64),\n",
    "        trainable=False)\n",
    "\n",
    "    fp_thresh_var = tf.get_variable(\n",
    "        name=\"fp_thresh_{0}_var\".format(var_name),\n",
    "        dtype=tf.int64,\n",
    "        initializer=tf.zeros(\n",
    "            shape=size, dtype=tf.int64),\n",
    "        trainable=False)\n",
    "\n",
    "    tn_thresh_var = tf.get_variable(\n",
    "        name=\"tn_thresh_{0}_var\".format(var_name),\n",
    "        dtype=tf.int64,\n",
    "        initializer=tf.zeros(\n",
    "            shape=size, dtype=tf.int64),\n",
    "        trainable=False)\n",
    "\n",
    "    return (tp_thresh_var,\n",
    "            fn_thresh_var,\n",
    "            fp_thresh_var,\n",
    "            tn_thresh_var)\n",
    "\n",
    "\n",
    "def create_both_confusion_matrix_thresh_vars(\n",
    "    scope, time_thresh_size, feat_thresh_size):\n",
    "  \"\"\"Creates both time & feature major confusion matrix threshold variables.\n",
    "\n",
    "  Given variable scope and sizes, create and return confusion\n",
    "  matrix threshold variables for true positives, false negatives, false\n",
    "  positives, and true negatives for both time and feature major\n",
    "  representations.\n",
    "\n",
    "  Args:\n",
    "    scope: String of variable scope name.\n",
    "    time_thresh_size: Variable size of number of time major thresholds.\n",
    "    feat_thresh_size: Variable size of number of feature major thresholds.\n",
    "\n",
    "  Returns:\n",
    "    Confusion matrix threshold variables for true positives, false negatives,\n",
    "    false positives, true negatives for both time and feature major\n",
    "    representations.\n",
    "  \"\"\"\n",
    "  # Time based\n",
    "  (tp_thresh_time_var,\n",
    "   fn_thresh_time_var,\n",
    "   fp_thresh_time_var,\n",
    "   tn_thresh_time_var) = create_confusion_matrix_thresh_vars(\n",
    "       scope=scope, var_name=\"time\", size=time_thresh_size)\n",
    "\n",
    "  # Features based\n",
    "  (tp_thresh_feat_var,\n",
    "   fn_thresh_feat_var,\n",
    "   fp_thresh_feat_var,\n",
    "   tn_thresh_feat_var) = create_confusion_matrix_thresh_vars(\n",
    "       scope=scope, var_name=\"feat\", size=feat_thresh_size)\n",
    "\n",
    "  return (tp_thresh_time_var,\n",
    "          fn_thresh_time_var,\n",
    "          fp_thresh_time_var,\n",
    "          tn_thresh_time_var,\n",
    "          tp_thresh_feat_var,\n",
    "          fn_thresh_feat_var,\n",
    "          fp_thresh_feat_var,\n",
    "          tn_thresh_feat_var)\n",
    "\n",
    "\n",
    "def create_mahalanobis_unsupervised_thresh_vars(scope, var_name):\n",
    "  \"\"\"Creates mahalanobis unsupervised threshold variables.\n",
    "\n",
    "  Given variable scope and name, create and return mahalanobis unsupervised\n",
    "  threshold variables of mean and standard deviation.\n",
    "\n",
    "  Args:\n",
    "    scope: String of variable scope name.\n",
    "    var_name: String denoting which set of variables to create. Values are\n",
    "      \"time\" and \"feat\".\n",
    "\n",
    "  Returns:\n",
    "    Mahalanobis unsupervised threshold variables of count, mean, and standard\n",
    "    deviation.\n",
    "  \"\"\"\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=scope, reuse=tf.AUTO_REUSE):\n",
    "    count_thresh_var = tf.get_variable(\n",
    "        name=\"count_thresh_{0}_var\".format(var_name),\n",
    "        dtype=tf.int64,\n",
    "        initializer=tf.zeros(\n",
    "            shape=[], dtype=tf.int64),\n",
    "        trainable=False)\n",
    "\n",
    "    mean_thresh_var = tf.get_variable(\n",
    "        name=\"mean_thresh_{0}_var\".format(var_name),\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(\n",
    "            shape=[], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "    var_thresh_var = tf.get_variable(\n",
    "        name=\"var_thresh_{0}_var\".format(var_name),\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(\n",
    "            shape=[], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "    return (count_thresh_var,\n",
    "            mean_thresh_var,\n",
    "            var_thresh_var)\n",
    "\n",
    "\n",
    "def create_both_mahalanobis_unsupervised_thresh_vars(scope):\n",
    "  \"\"\"Creates time & feature mahalanobis unsupervised threshold variables.\n",
    "\n",
    "  Given variable scope, create and return mahalanobis unsupervised\n",
    "  threshold variables of mean and standard deviation for both time and\n",
    "  feature major representations.\n",
    "\n",
    "  Args:\n",
    "    scope: String of variable scope name.\n",
    "\n",
    "  Returns:\n",
    "    Mahalanobis unsupervised threshold variables of mean and standard\n",
    "    deviation for both time and feature major representations.\n",
    "  \"\"\"\n",
    "  # Time based\n",
    "  (count_thresh_time_var,\n",
    "   mean_thresh_time_var,\n",
    "   var_thresh_time_var) = create_mahalanobis_unsupervised_thresh_vars(\n",
    "       scope=scope, var_name=\"time\")\n",
    "\n",
    "  # Features based\n",
    "  (count_thresh_feat_var,\n",
    "   mean_thresh_feat_var,\n",
    "   var_thresh_feat_var) = create_mahalanobis_unsupervised_thresh_vars(\n",
    "       scope=scope, var_name=\"feat\")\n",
    "\n",
    "  return (count_thresh_time_var,\n",
    "          mean_thresh_time_var,\n",
    "          var_thresh_time_var,\n",
    "          count_thresh_feat_var,\n",
    "          mean_thresh_feat_var,\n",
    "          var_thresh_feat_var)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## tune_anomaly_thresholds_supervised.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculate_threshold_confusion_matrix(labels_mask, preds, num_thresh):\n",
    "  \"\"\"Calculates confusion matrix based on thresholds.\n",
    "\n",
    "  Given labels mask, predictions, and number of thresholds, returns count\n",
    "  for cell in confusion matrix.\n",
    "\n",
    "  Args:\n",
    "    labels_mask: tf.bool vector tensor when label was normal or\n",
    "      anomalous.\n",
    "    preds: Predicted anomaly labels.\n",
    "    num_thresh: Number of anomaly thresholds to try in parallel grid search.\n",
    "\n",
    "  Returns:\n",
    "    Count for cell in confusion matrix.\n",
    "  \"\"\"\n",
    "  count = tf.reduce_sum(\n",
    "      input_tensor=tf.cast(\n",
    "          x=tf.map_fn(\n",
    "              fn=lambda threshold: tf.logical_and(\n",
    "                  x=labels_mask,\n",
    "                  y=preds[threshold, :]),\n",
    "              elems=tf.range(start=0, limit=num_thresh, dtype=tf.int64),\n",
    "              dtype=tf.bool),\n",
    "          dtype=tf.int64),\n",
    "      axis=1)\n",
    "\n",
    "  return count\n",
    "\n",
    "\n",
    "def update_anom_thresh_vars(\n",
    "    labels_norm_mask,\n",
    "    labels_anom_mask,\n",
    "    num_thresh,\n",
    "    anom_thresh,\n",
    "    mahalanobis_dist,\n",
    "    tp_at_thresh_var,\n",
    "    fn_at_thresh_var,\n",
    "    fp_at_thresh_var,\n",
    "    tn_at_thresh_var,\n",
    "    mode):\n",
    "  \"\"\"Updates anomaly threshold variables.\n",
    "\n",
    "  Given masks for when labels are normal and anomalous, the number of anomaly\n",
    "  thresholds and the thresholds themselves, the mahalanobis distance, variables\n",
    "  for the confusion matrix, and the current Estimator mode, returns the updated\n",
    "  variables for the confusion matrix.\n",
    "\n",
    "  Args:\n",
    "    labels_norm_mask: tf.bool vector tensor that is true when label was normal.\n",
    "    labels_anom_mask: tf.bool vector tensor that is true when label was\n",
    "      anomalous.\n",
    "    num_thresh: Number of anomaly thresholds to try in parallel grid search.\n",
    "    anom_thresh: tf.float64 vector tensor of grid of anomaly thresholds to try.\n",
    "    mahalanobis_dist: tf.float64 matrix tensor of mahalanobis distances across\n",
    "      batch.\n",
    "    tp_at_thresh_var: tf.int64 variable tracking number of true positives at\n",
    "      each possible anomaly threshold.\n",
    "    fn_at_thresh_var: tf.int64 variable tracking number of false negatives at\n",
    "      each possible anomaly threshold.\n",
    "    fp_at_thresh_var: tf.int64 variable tracking number of false positives at\n",
    "      each possible anomaly threshold.\n",
    "    tn_at_thresh_var: tf.int64 variable tracking number of true negatives at\n",
    "      each possible anomaly threshold.\n",
    "    mode: Estimator ModeKeys, can take values of TRAIN and EVAL.\n",
    "\n",
    "  Returns:\n",
    "    Updated confusion matrix variables.\n",
    "  \"\"\"\n",
    "  if mode == tf.estimator.ModeKeys.TRAIN:\n",
    "    # time_shape = (num_time_anom_thresh, cur_batch_size, seq_len)\n",
    "    # feat_shape = (num_feat_anom_thresh, cur_batch_size, num_feat)\n",
    "    mahalanobis_dist_over_thresh = tf.map_fn(\n",
    "        fn=lambda anom_threshold: mahalanobis_dist > anom_threshold,\n",
    "        elems=anom_thresh,\n",
    "        dtype=tf.bool)\n",
    "  else:\n",
    "    # time_shape = (cur_batch_size, seq_len)\n",
    "    # feat_shape = (cur_batch_size, num_feat)\n",
    "    mahalanobis_dist_over_thresh = mahalanobis_dist > anom_thresh\n",
    "\n",
    "  # time_shape = (num_time_anom_thresh, cur_batch_size)\n",
    "  # feat_shape = (num_feat_anom_thresh, cur_batch_size)\n",
    "  mahalanobis_dist_any_over_thresh = tf.reduce_any(\n",
    "      input_tensor=mahalanobis_dist_over_thresh, axis=-1)\n",
    "\n",
    "  if mode == tf.estimator.ModeKeys.EVAL:\n",
    "    # time_shape = (1, cur_batch_size)\n",
    "    # feat_shape = (1, cur_batch_size)\n",
    "    mahalanobis_dist_any_over_thresh = tf.expand_dims(\n",
    "        input=mahalanobis_dist_any_over_thresh, axis=0)\n",
    "\n",
    "  # time_shape = (num_time_anom_thresh, cur_batch_size)\n",
    "  # feat_shape = (num_feat_anom_thresh, cur_batch_size)\n",
    "  predicted_normals = tf.equal(\n",
    "      x=mahalanobis_dist_any_over_thresh, y=False)\n",
    "\n",
    "  # time_shape = (num_time_anom_thresh, cur_batch_size)\n",
    "  # feat_shape = (num_feat_anom_thresh, cur_batch_size)\n",
    "  predicted_anomalies = tf.equal(\n",
    "      x=mahalanobis_dist_any_over_thresh, y=True)\n",
    "\n",
    "  # Calculate confusion matrix of current batch\n",
    "  # time_shape = (num_time_anom_thresh,)\n",
    "  # feat_shape = (num_feat_anom_thresh,)\n",
    "  tp = calculate_threshold_confusion_matrix(\n",
    "      labels_anom_mask, predicted_anomalies, num_thresh)\n",
    "\n",
    "  fn = calculate_threshold_confusion_matrix(\n",
    "      labels_anom_mask, predicted_normals, num_thresh)\n",
    "\n",
    "  fp = calculate_threshold_confusion_matrix(\n",
    "      labels_norm_mask, predicted_anomalies, num_thresh)\n",
    "\n",
    "  tn = calculate_threshold_confusion_matrix(\n",
    "      labels_norm_mask, predicted_normals, num_thresh)\n",
    "\n",
    "  if mode == tf.estimator.ModeKeys.EVAL:\n",
    "    # shape = ()\n",
    "    tp = tf.squeeze(input=tp)\n",
    "    fn = tf.squeeze(input=fn)\n",
    "    fp = tf.squeeze(input=fp)\n",
    "    tn = tf.squeeze(input=tn)\n",
    "\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[tf.assign_add(ref=tp_at_thresh_var, value=tp),\n",
    "                      tf.assign_add(ref=fn_at_thresh_var, value=fn),\n",
    "                      tf.assign_add(ref=fp_at_thresh_var, value=fp),\n",
    "                      tf.assign_add(ref=tn_at_thresh_var, value=tn)]):\n",
    "\n",
    "    return (tf.identity(input=tp_at_thresh_var),\n",
    "            tf.identity(input=fn_at_thresh_var),\n",
    "            tf.identity(input=fp_at_thresh_var),\n",
    "            tf.identity(input=tn_at_thresh_var))\n",
    "\n",
    "\n",
    "def calculate_composite_classification_metrics(tp, fn, fp, tn, f_score_beta):\n",
    "  \"\"\"Calculates compositive classification metrics from the confusion matrix.\n",
    "\n",
    "  Given variables for the confusion matrix and the value of beta for f-beta\n",
    "  score, returns accuracy, precision, recall, and f-beta score composite\n",
    "  metrics.\n",
    "\n",
    "  Args:\n",
    "    tp: tf.int64 variable tracking number of true positives at\n",
    "      each possible anomaly threshold.\n",
    "    fn: tf.int64 variable tracking number of false negatives at\n",
    "      each possible anomaly threshold.\n",
    "    fp: tf.int64 variable tracking number of false positives at\n",
    "      each possible anomaly threshold.\n",
    "    tn: tf.int64 variable tracking number of true negatives at\n",
    "      each possible anomaly threshold.\n",
    "    f_score_beta: Value of beta for f-beta score.\n",
    "\n",
    "  Returns:\n",
    "    Accuracy, precision, recall, and f-beta score composite metric tensors.\n",
    "  \"\"\"\n",
    "  # time_shape = (num_time_anom_thresh,)\n",
    "  # feat_shape = (num_feat_anom_thresh,)\n",
    "  acc = tf.cast(x=tp + tn, dtype=tf.float64) \\\n",
    "    / tf.cast(x=tp + fn + fp + tn, dtype=tf.float64)\n",
    "  tp_float64 = tf.cast(x=tp, dtype=tf.float64)\n",
    "  pre = tp_float64 / tf.cast(x=tp + fp, dtype=tf.float64)\n",
    "  rec = tp_float64 / tf.cast(x=tp + fn, dtype=tf.float64)\n",
    "  f_beta_numerator = (1.0 + f_score_beta ** 2) * (pre * rec)\n",
    "  f_beta_score = f_beta_numerator / (f_score_beta ** 2 * pre + rec)\n",
    "\n",
    "  return acc, pre, rec, f_beta_score\n",
    "\n",
    "\n",
    "def find_best_anom_thresh(\n",
    "    anom_threshs, f_beta_score, anom_thresh_var):\n",
    "  \"\"\"Find best anomaly threshold to use for anomaly classification.\n",
    "\n",
    "  Given vector of anomaly thresholds and the value of beta for f-beta score,\n",
    "  returns updated variable that stores the best anomaly threshold value.\n",
    "\n",
    "  Args:\n",
    "    anom_threshs: tf.float64 vector tensor of grid of anomaly thresholds to try.\n",
    "    f_beta_score: tf.float64 vector tensor of f-beta scores for each anomaly\n",
    "      threshold.\n",
    "    anom_thresh_var: tf.float64 variable that stores anomaly threshold value.\n",
    "\n",
    "  Returns:\n",
    "    Updated variable that stores the anomaly threshold value.\n",
    "  \"\"\"\n",
    "  # shape = ()\n",
    "  best_anom_thresh = tf.gather(\n",
    "      params=anom_threshs, indices=tf.argmax(input=f_beta_score, axis=0))\n",
    "\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[tf.assign(\n",
    "          ref=anom_thresh_var, value=best_anom_thresh)]):\n",
    "\n",
    "    return tf.identity(input=anom_thresh_var)\n",
    "\n",
    "\n",
    "def optimize_anomaly_theshold(\n",
    "    var_name,\n",
    "    labels_norm_mask,\n",
    "    labels_anom_mask,\n",
    "    mahalanobis_dist,\n",
    "    tp_thresh_var,\n",
    "    fn_thresh_var,\n",
    "    fp_thresh_var,\n",
    "    tn_thresh_var,\n",
    "    params,\n",
    "    mode,\n",
    "    anom_thresh_var):\n",
    "  \"\"\"Optimizes anomaly threshold for anomaly classification.\n",
    "\n",
    "  Given variable name, label masks, mahalanobis distance, variables for\n",
    "  confusion matrix, and dictionary of parameters, returns accuracy, precision,\n",
    "  recall, and f-beta score composite metrics.\n",
    "\n",
    "  Args:\n",
    "    var_name: String denoting which set of variables to use. Values are\n",
    "      \"time\" and \"feat\".\n",
    "    labels_norm_mask: tf.bool vector mask of labels for normals.\n",
    "    labels_anom_mask: tf.bool vector mask of labels for anomalies.\n",
    "    mahalanobis_dist: Mahalanobis distance of reconstruction error.\n",
    "    tp_thresh_var: tf.int64 variable to track number of true positives wrt\n",
    "      thresholds.\n",
    "    fn_thresh_var: tf.int64 variable to track number of false negatives wrt\n",
    "      thresholds.\n",
    "    fp_thresh_var: tf.int64 variable to track number of false positives wrt\n",
    "      thresholds.\n",
    "    tn_thresh_var: tf.int64 variable to track number of true negatives wrt\n",
    "      thresholds.\n",
    "    params: Dictionary of parameters.\n",
    "    mode: Estimator ModeKeys, can take values of TRAIN and EVAL.\n",
    "    anom_thresh_var: tf.float64 variable that stores anomaly threshold value.\n",
    "\n",
    "  Returns:\n",
    "    Updated variable that stores the anomaly threshold value\n",
    "  \"\"\"\n",
    "  # shape = (num_anom_thresh,)\n",
    "  anom_threshs = tf.linspace(\n",
    "      start=tf.constant(\n",
    "          value=params[\"min_{}_anom_thresh\".format(var_name)],\n",
    "          dtype=tf.float64),\n",
    "      stop=tf.constant(\n",
    "          value=params[\"max_{}_anom_thresh\".format(var_name)],\n",
    "          dtype=tf.float64),\n",
    "      num=params[\"num_{}_anom_thresh\".format(var_name)])\n",
    "\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=\"mahalanobis_dist_thresh_vars\",\n",
    "      reuse=tf.AUTO_REUSE):\n",
    "    (tp_update_op,\n",
    "     fn_update_op,\n",
    "     fp_update_op,\n",
    "     tn_update_op) = \\\n",
    "      update_anom_thresh_vars(\n",
    "          labels_norm_mask,\n",
    "          labels_anom_mask,\n",
    "          params[\"num_{}_anom_thresh\".format(var_name)],\n",
    "          anom_threshs,\n",
    "          mahalanobis_dist,\n",
    "          tp_thresh_var,\n",
    "          fn_thresh_var,\n",
    "          fp_thresh_var,\n",
    "          tn_thresh_var,\n",
    "          mode)\n",
    "\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[\n",
    "          tp_update_op,\n",
    "          fn_update_op,\n",
    "          fp_update_op,\n",
    "          tn_update_op]):\n",
    "    _, pre, rec, f_beta = \\\n",
    "      calculate_composite_classification_metrics(\n",
    "          tp_thresh_var,\n",
    "          fn_thresh_var,\n",
    "          fp_thresh_var,\n",
    "          tn_thresh_var,\n",
    "          params[\"f_score_beta\"])\n",
    "\n",
    "    with tf.control_dependencies(control_inputs=[pre, rec]):\n",
    "      with tf.control_dependencies(control_inputs=[f_beta]):\n",
    "        best_anom_thresh = find_best_anom_thresh(\n",
    "            anom_threshs,\n",
    "            f_beta,\n",
    "            anom_thresh_var)\n",
    "        with tf.control_dependencies(control_inputs=[best_anom_thresh]):\n",
    "          return tf.identity(input=anom_thresh_var)\n",
    "\n",
    "\n",
    "def set_anom_thresh(user_passed_anom_thresh, anom_thresh_var):\n",
    "  \"\"\"Set anomaly threshold to use for anomaly classification from user input.\n",
    "\n",
    "  Given user passed anomaly threshold returns updated variable that stores\n",
    "  the anomaly threshold value.\n",
    "\n",
    "  Args:\n",
    "    user_passed_anom_thresh: User passed anomaly threshold that overrides\n",
    "      the threshold optimization.\n",
    "    anom_thresh_var: tf.float64 variable that stores anomaly threshold value.\n",
    "\n",
    "  Returns:\n",
    "    Updated variable that stores the anomaly threshold value.\n",
    "  \"\"\"\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[tf.assign(\n",
    "          ref=anom_thresh_var, value=user_passed_anom_thresh)]):\n",
    "\n",
    "    return tf.identity(input=anom_thresh_var)\n",
    "\n",
    "\n",
    "def tune_anomaly_thresholds_supervised_training(\n",
    "    labels_norm_mask,\n",
    "    labels_anom_mask,\n",
    "    mahalanobis_dist_time,\n",
    "    tp_thresh_time_var,\n",
    "    fn_thresh_time_var,\n",
    "    fp_thresh_time_var,\n",
    "    tn_thresh_time_var,\n",
    "    time_anom_thresh_var,\n",
    "    mahalanobis_dist_feat,\n",
    "    tp_thresh_feat_var,\n",
    "    fn_thresh_feat_var,\n",
    "    fp_thresh_feat_var,\n",
    "    tn_thresh_feat_var,\n",
    "    feat_anom_thresh_var,\n",
    "    params,\n",
    "    mode,\n",
    "    dummy_var):\n",
    "  \"\"\"Tunes anomaly thresholds during supervised training mode.\n",
    "\n",
    "  Given label masks, mahalanobis distances, confusion matrices, and anomaly\n",
    "  thresholds, returns loss and train_op.\n",
    "\n",
    "  Args:\n",
    "    labels_norm_mask: tf.bool vector mask of labels for normals.\n",
    "    labels_anom_mask: tf.bool vector mask of labels for anomalies.\n",
    "    mahalanobis_dist_time: Mahalanobis distance, time major.\n",
    "    tp_thresh_time_var: tf.int64 variable to track number of true positives wrt\n",
    "      thresholds for time major case.\n",
    "    fn_thresh_time_var: tf.int64 variable to track number of false negatives wrt\n",
    "      thresholds for time major case.\n",
    "    fp_thresh_time_var: tf.int64 variable to track number of false positives wrt\n",
    "      thresholds for time major case.\n",
    "    tn_thresh_time_var: tf.int64 variable to track number of true negatives wrt\n",
    "      thresholds for time major case.\n",
    "    time_anom_thresh_var: tf.float64 variable to hold the set time anomaly\n",
    "      threshold.\n",
    "    mahalanobis_dist_feat: Mahalanobis distance, features major.\n",
    "    tp_thresh_feat_var: tf.int64 variable to track number of true positives wrt\n",
    "      thresholds for feat major case.\n",
    "    fn_thresh_feat_var: tf.int64 variable to track number of false negatives wrt\n",
    "      thresholds for feat major case.\n",
    "    fp_thresh_feat_var: tf.int64 variable to track number of false positives wrt\n",
    "      thresholds for feat major case.\n",
    "    tn_thresh_feat_var: tf.int64 variable to track number of true negatives wrt\n",
    "      thresholds for feat major case.\n",
    "    feat_anom_thresh_var: tf.float64 variable to hold the set feat anomaly\n",
    "      threshold.\n",
    "    params: Dictionary of parameters.\n",
    "    mode: Estimator ModeKeys. Can take value of only TRAIN.\n",
    "    dummy_var: Dummy variable used to allow training mode to happen since it\n",
    "      requires a gradient to tie back to the graph dependency.\n",
    "\n",
    "  Returns:\n",
    "    loss: The scalar loss to tie our updates back to Estimator graph.\n",
    "    train_op: The train operation to tie our updates back to Estimator graph.\n",
    "  \"\"\"\n",
    "  # Time based\n",
    "  if params[\"time_anom_thresh\"] is None:\n",
    "    best_anom_thresh_time = optimize_anomaly_theshold(\n",
    "        \"time\",\n",
    "        labels_norm_mask,\n",
    "        labels_anom_mask,\n",
    "        mahalanobis_dist_time,\n",
    "        tp_thresh_time_var,\n",
    "        fn_thresh_time_var,\n",
    "        fp_thresh_time_var,\n",
    "        tn_thresh_time_var,\n",
    "        params,\n",
    "        mode,\n",
    "        time_anom_thresh_var)\n",
    "  else:\n",
    "    best_anom_thresh_time = set_anom_thresh(\n",
    "        params[\"time_anom_thresh\"], time_anom_thresh_var)\n",
    "\n",
    "  # Features based\n",
    "  if params[\"feat_anom_thresh\"] is None:\n",
    "    best_anom_thresh_feat = optimize_anomaly_theshold(\n",
    "        \"feat\",\n",
    "        labels_norm_mask,\n",
    "        labels_anom_mask,\n",
    "        mahalanobis_dist_feat,\n",
    "        tp_thresh_feat_var,\n",
    "        fn_thresh_feat_var,\n",
    "        fp_thresh_feat_var,\n",
    "        tn_thresh_feat_var,\n",
    "        params,\n",
    "        mode,\n",
    "        feat_anom_thresh_var)\n",
    "  else:\n",
    "    best_anom_thresh_feat = set_anom_thresh(\n",
    "        params[\"feat_anom_thresh\"], feat_anom_thresh_var)\n",
    "\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[best_anom_thresh_time,\n",
    "                      best_anom_thresh_feat]):\n",
    "    loss = tf.reduce_sum(\n",
    "        input_tensor=tf.zeros(\n",
    "            shape=(), dtype=tf.float64) * dummy_var)\n",
    "\n",
    "    train_op = tf.contrib.layers.optimize_loss(\n",
    "        loss=loss,\n",
    "        global_step=tf.train.get_global_step(),\n",
    "        learning_rate=params[\"learning_rate\"],\n",
    "        optimizer=\"SGD\")\n",
    "\n",
    "    return loss, train_op\n",
    "\n",
    "\n",
    "def tune_anomaly_thresholds_supervised_eval(\n",
    "    labels_norm_mask,\n",
    "    labels_anom_mask,\n",
    "    time_anom_thresh_var,\n",
    "    mahalanobis_dist_time,\n",
    "    tp_thresh_eval_time_var,\n",
    "    fn_thresh_eval_time_var,\n",
    "    fp_thresh_eval_time_var,\n",
    "    tn_thresh_eval_time_var,\n",
    "    feat_anom_thresh_var,\n",
    "    mahalanobis_dist_feat,\n",
    "    tp_thresh_eval_feat_var,\n",
    "    fn_thresh_eval_feat_var,\n",
    "    fp_thresh_eval_feat_var,\n",
    "    tn_thresh_eval_feat_var,\n",
    "    params,\n",
    "    mode):\n",
    "  \"\"\"Checks tuned anomaly thresholds during supervised evaluation mode.\n",
    "\n",
    "  Given label masks, mahalanobis distances, confusion matrices, and anomaly\n",
    "  thresholds, returns loss and eval_metric_ops.\n",
    "\n",
    "  Args:\n",
    "    labels_norm_mask: tf.bool vector mask of labels for normals.\n",
    "    labels_anom_mask: tf.bool vector mask of labels for anomalies.\n",
    "    time_anom_thresh_var: tf.float64 scalar time anomaly threshold value.\n",
    "    mahalanobis_dist_time: Mahalanobis distance, time major.\n",
    "    tp_thresh_eval_time_var: tf.int64 variable to track number of true\n",
    "      positives wrt thresholds for time major case for evaluation.\n",
    "    fn_thresh_eval_time_var: tf.int64 variable to track number of false\n",
    "      negatives wrt thresholds for time major case for evaluation.\n",
    "    fp_thresh_eval_time_var: tf.int64 variable to track number of false\n",
    "      positives wrt thresholds for time major case for evaluation.\n",
    "    tn_thresh_eval_time_var: tf.int64 variable to track number of true\n",
    "      negatives wrt thresholds for time major case for evaluation.\n",
    "    feat_anom_thresh_var: tf.float64 scalar feature anomaly threshold value.\n",
    "    mahalanobis_dist_feat: Mahalanobis distance, features major.\n",
    "    tp_thresh_eval_feat_var: tf.int64 variable to track number of true\n",
    "      positives wrt thresholds for feat major case for evaluation.\n",
    "    fn_thresh_eval_feat_var: tf.int64 variable to track number of false\n",
    "      negatives wrt thresholds for feat major case for evaluation.\n",
    "    fp_thresh_eval_feat_var: tf.int64 variable to track number of false\n",
    "      positives wrt thresholds for feat major case for evaluation.\n",
    "    tn_thresh_eval_feat_var: tf.int64 variable to track number of true\n",
    "      negatives wrt thresholds for feat major case for evaluation.\n",
    "    params: Dictionary of parameters.\n",
    "    mode: Estimator ModeKeys. Can take value of only EVAL.\n",
    "\n",
    "  Returns:\n",
    "    loss: Scalar reconstruction loss.\n",
    "    eval_metric_ops: Evaluation metrics of threshold tuning.\n",
    "  \"\"\"\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=\"anom_thresh_eval_vars\", reuse=tf.AUTO_REUSE):\n",
    "    # Time based\n",
    "    (tp_time_update_op,\n",
    "     fn_time_update_op,\n",
    "     fp_time_update_op,\n",
    "     tn_time_update_op) = \\\n",
    "      update_anom_thresh_vars(\n",
    "          labels_norm_mask,\n",
    "          labels_anom_mask,\n",
    "          1,\n",
    "          time_anom_thresh_var,\n",
    "          mahalanobis_dist_time,\n",
    "          tp_thresh_eval_time_var,\n",
    "          fn_thresh_eval_time_var,\n",
    "          fp_thresh_eval_time_var,\n",
    "          tn_thresh_eval_time_var,\n",
    "          mode)\n",
    "\n",
    "    # Features based\n",
    "    (tp_feat_update_op,\n",
    "     fn_feat_update_op,\n",
    "     fp_feat_update_op,\n",
    "     tn_feat_update_op) = \\\n",
    "      update_anom_thresh_vars(\n",
    "          labels_norm_mask,\n",
    "          labels_anom_mask,\n",
    "          1,\n",
    "          feat_anom_thresh_var,\n",
    "          mahalanobis_dist_feat,\n",
    "          tp_thresh_eval_feat_var,\n",
    "          fn_thresh_eval_feat_var,\n",
    "          fp_thresh_eval_feat_var,\n",
    "          tn_thresh_eval_feat_var,\n",
    "          mode)\n",
    "\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=\"anom_thresh_eval_vars\", reuse=tf.AUTO_REUSE):\n",
    "    # Time based\n",
    "    (acc_time_update_op,\n",
    "     pre_time_update_op,\n",
    "     rec_time_update_op,\n",
    "     f_beta_time_update_op) = \\\n",
    "      calculate_composite_classification_metrics(\n",
    "          tp_thresh_eval_time_var,\n",
    "          fn_thresh_eval_time_var,\n",
    "          fp_thresh_eval_time_var,\n",
    "          tn_thresh_eval_time_var,\n",
    "          params[\"f_score_beta\"])\n",
    "\n",
    "    # Features based\n",
    "    (acc_feat_update_op,\n",
    "     pre_feat_update_op,\n",
    "     rec_feat_update_op,\n",
    "     f_beta_feat_update_op) = \\\n",
    "      calculate_composite_classification_metrics(\n",
    "          tp_thresh_eval_feat_var,\n",
    "          fn_thresh_eval_feat_var,\n",
    "          fp_thresh_eval_feat_var,\n",
    "          tn_thresh_eval_feat_var,\n",
    "          params[\"f_score_beta\"])\n",
    "\n",
    "  loss = tf.zeros(shape=[], dtype=tf.float64)\n",
    "\n",
    "  # Time based\n",
    "  acc_trues = tf.cast(\n",
    "      x=tp_thresh_eval_time_var + tn_thresh_eval_time_var,\n",
    "      dtype=tf.float64)\n",
    "  acc_falses = tf.cast(\n",
    "      x=fp_thresh_eval_time_var + fn_thresh_eval_time_var,\n",
    "      dtype=tf.float64)\n",
    "  acc_thresh_eval_time_var = acc_trues / (acc_trues + acc_falses)\n",
    "\n",
    "  tp_float = tf.cast(x=tp_thresh_eval_time_var, dtype=tf.float64)\n",
    "\n",
    "  pre_denominator = tf.cast(\n",
    "      x=tp_thresh_eval_time_var + fp_thresh_eval_time_var,\n",
    "      dtype=tf.float64)\n",
    "  pre_thresh_eval_time_var = tp_float / pre_denominator\n",
    "\n",
    "  rec_denominator = tf.cast(\n",
    "      x=tp_thresh_eval_time_var + fn_thresh_eval_time_var,\n",
    "      dtype=tf.float64)\n",
    "  rec_thresh_eval_time_var = tp_float / rec_denominator\n",
    "\n",
    "  f_beta_numerator = (1.0 + params[\"f_score_beta\"] ** 2)\n",
    "  f_beta_numerator *= pre_thresh_eval_time_var\n",
    "  f_beta_numerator *= rec_thresh_eval_time_var\n",
    "  f_beta_denominator = params[\"f_score_beta\"] ** 2\n",
    "  f_beta_denominator *= pre_thresh_eval_time_var\n",
    "  f_beta_denominator += rec_thresh_eval_time_var\n",
    "  f_beta_thresh_eval_time_var = f_beta_numerator / f_beta_denominator\n",
    "\n",
    "  # Features based\n",
    "  acc_trues = tf.cast(\n",
    "      x=tp_thresh_eval_feat_var + tn_thresh_eval_feat_var,\n",
    "      dtype=tf.float64)\n",
    "  acc_falses = tf.cast(\n",
    "      x=fp_thresh_eval_feat_var + fn_thresh_eval_feat_var,\n",
    "      dtype=tf.float64)\n",
    "  acc_thresh_eval_feat_var = acc_trues / (acc_trues + acc_falses)\n",
    "\n",
    "  tp_float = tf.cast(x=tp_thresh_eval_feat_var, dtype=tf.float64)\n",
    "\n",
    "  pre_denominator = tf.cast(\n",
    "      x=tp_thresh_eval_feat_var + fp_thresh_eval_feat_var,\n",
    "      dtype=tf.float64)\n",
    "  pre_thresh_eval_feat_var = tp_float / pre_denominator\n",
    "\n",
    "  rec_denominator = tf.cast(\n",
    "      x=tp_thresh_eval_feat_var + fn_thresh_eval_feat_var,\n",
    "      dtype=tf.float64)\n",
    "  rec_thresh_eval_feat_var = tp_float / rec_denominator\n",
    "\n",
    "  f_beta_numerator = (1.0 + params[\"f_score_beta\"] ** 2)\n",
    "  f_beta_numerator *= pre_thresh_eval_feat_var\n",
    "  f_beta_numerator *= rec_thresh_eval_feat_var\n",
    "  f_beta_denominator = params[\"f_score_beta\"] ** 2\n",
    "  f_beta_denominator *= pre_thresh_eval_feat_var\n",
    "  f_beta_denominator += rec_thresh_eval_feat_var\n",
    "  f_beta_thresh_eval_feat_var = f_beta_numerator / f_beta_denominator\n",
    "\n",
    "  # Anomaly detection eval metrics\n",
    "  eval_metric_ops = {\n",
    "      # Time based\n",
    "      \"time_anom_tp\": (tp_thresh_eval_time_var, tp_time_update_op),\n",
    "      \"time_anom_fn\": (fn_thresh_eval_time_var, fn_time_update_op),\n",
    "      \"time_anom_fp\": (fp_thresh_eval_time_var, fp_time_update_op),\n",
    "      \"time_anom_tn\": (tn_thresh_eval_time_var, tn_time_update_op),\n",
    "\n",
    "      \"time_anom_acc\": (acc_thresh_eval_time_var, acc_time_update_op),\n",
    "      \"time_anom_pre\": (pre_thresh_eval_time_var, pre_time_update_op),\n",
    "      \"time_anom_rec\": (rec_thresh_eval_time_var, rec_time_update_op),\n",
    "      \"time_anom_f_beta\": (f_beta_thresh_eval_time_var,\n",
    "                           f_beta_time_update_op),\n",
    "\n",
    "      # Features based\n",
    "      \"feat_anom_tp\": (tp_thresh_eval_feat_var, tp_feat_update_op),\n",
    "      \"feat_anom_fn\": (fn_thresh_eval_feat_var, fn_feat_update_op),\n",
    "      \"feat_anom_fp\": (fp_thresh_eval_feat_var, fp_feat_update_op),\n",
    "      \"feat_anom_tn\": (tn_thresh_eval_feat_var, tn_feat_update_op),\n",
    "\n",
    "      \"feat_anom_acc\": (acc_thresh_eval_feat_var, acc_feat_update_op),\n",
    "      \"feat_anom_pre\": (pre_thresh_eval_feat_var, pre_feat_update_op),\n",
    "      \"feat_anom_rec\": (rec_thresh_eval_feat_var, rec_feat_update_op),\n",
    "      \"feat_anom_f_beta\": (f_beta_thresh_eval_feat_var,\n",
    "                           f_beta_feat_update_op)\n",
    "  }\n",
    "\n",
    "  return loss, eval_metric_ops"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## tune_anomaly_thresholds_unsupervised.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "def tune_anomaly_thresholds_unsupervised_training(\n",
    "    cur_batch_size,\n",
    "    time_anom_thresh_var,\n",
    "    mahalanobis_dist_time,\n",
    "    count_thresh_time_var,\n",
    "    mean_thresh_time_var,\n",
    "    var_thresh_time_var,\n",
    "    feat_anom_thresh_var,\n",
    "    mahalanobis_dist_feat,\n",
    "    count_thresh_feat_var,\n",
    "    mean_thresh_feat_var,\n",
    "    var_thresh_feat_var,\n",
    "    params,\n",
    "    dummy_var):\n",
    "  \"\"\"Tunes anomaly thresholds during unsupervised training mode.\n",
    "\n",
    "  Given dimensions of inputs, mahalanobis distances, and variables tracking\n",
    "  counts, means, and variances of mahalanobis distance, returns loss and\n",
    "  train_op.\n",
    "\n",
    "  Args:\n",
    "    cur_batch_size: Current batch size, could be partially filled.\n",
    "    time_anom_thresh_var: Time anomaly threshold variable.\n",
    "    mahalanobis_dist_time: Time major mahalanobis distance.\n",
    "    count_thresh_time_var: Time major running count of number of records.\n",
    "    mean_thresh_time_var: Time major running mean of mahalanobis distance.\n",
    "    var_thresh_time_var: Time major running variance of mahalanobis distance.\n",
    "    feat_anom_thresh_var: Feature anomaly threshold variable.\n",
    "    mahalanobis_dist_feat: Feature major mahalanobis distance.\n",
    "    count_thresh_feat_var: Feature major running count of number of records.\n",
    "    mean_thresh_feat_var: Feature major running mean of mahalanobis distance.\n",
    "    var_thresh_feat_var: Feature major running variance of mahalanobis distance.\n",
    "    params: Dictionary of parameters.\n",
    "    dummy_var: Dummy variable used to allow training mode to happen since it\n",
    "      requires a gradient to tie back to the graph dependency.\n",
    "\n",
    "  Returns:\n",
    "    loss: The scalar loss to tie our updates back to Estimator graph.\n",
    "    train_op: The train operation to tie our updates back to Estimator graph.\n",
    "  \"\"\"\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=\"mahalanobis_dist_thresh_vars\", reuse=tf.AUTO_REUSE):\n",
    "    # Time based\n",
    "    mahalanobis_dist_time_flat = tf.reshape(\n",
    "        tensor=mahalanobis_dist_time,\n",
    "        shape=[cur_batch_size * params[\"seq_len\"]])\n",
    "\n",
    "    singleton_time_condition = tf.equal(\n",
    "        x=cur_batch_size * params[\"seq_len\"], y=1)\n",
    "\n",
    "    var_time_var, mean_time_var, count_time_var = tf.cond(\n",
    "        pred=singleton_time_condition,\n",
    "        true_fn=lambda: singleton_batch_var_variable_updating(\n",
    "            params[\"seq_len\"],\n",
    "            mahalanobis_dist_time_flat,\n",
    "            count_thresh_time_var,\n",
    "            mean_thresh_time_var,\n",
    "            var_thresh_time_var),\n",
    "        false_fn=lambda: non_singleton_batch_var_variable_updating(\n",
    "            cur_batch_size,\n",
    "            params[\"seq_len\"],\n",
    "            mahalanobis_dist_time_flat,\n",
    "            count_thresh_time_var,\n",
    "            mean_thresh_time_var,\n",
    "            var_thresh_time_var))\n",
    "\n",
    "    # Features based\n",
    "    mahalanobis_dist_feat_flat = tf.reshape(\n",
    "        tensor=mahalanobis_dist_feat,\n",
    "        shape=[cur_batch_size * params[\"num_feat\"]])\n",
    "\n",
    "    singleton_feat_condition = tf.equal(\n",
    "        x=cur_batch_size * params[\"num_feat\"], y=1)\n",
    "\n",
    "    var_feat_var, mean_feat_var, count_feat_var = tf.cond(\n",
    "        pred=singleton_feat_condition,\n",
    "        true_fn=lambda: singleton_batch_var_variable_updating(\n",
    "            params[\"num_feat\"],\n",
    "            mahalanobis_dist_feat_flat,\n",
    "            count_thresh_feat_var,\n",
    "            mean_thresh_feat_var,\n",
    "            var_thresh_feat_var),\n",
    "        false_fn=lambda: non_singleton_batch_var_variable_updating(\n",
    "            cur_batch_size,\n",
    "            params[\"num_feat\"],\n",
    "            mahalanobis_dist_feat_flat,\n",
    "            count_thresh_feat_var,\n",
    "            mean_thresh_feat_var,\n",
    "            var_thresh_feat_var))\n",
    "\n",
    "  # Lastly use control dependencies around loss to enforce the mahalanobis\n",
    "  # variables to be assigned, the control order matters, hence the separate\n",
    "  # contexts.\n",
    "  with tf.control_dependencies(\n",
    "      control_inputs=[var_time_var, var_feat_var]):\n",
    "    with tf.control_dependencies(\n",
    "        control_inputs=[mean_time_var, mean_feat_var]):\n",
    "      with tf.control_dependencies(\n",
    "          control_inputs=[count_time_var, count_feat_var]):\n",
    "        time_out = mean_time_var\n",
    "        time_out += params[\"time_thresh_scl\"] * tf.sqrt(x=var_time_var)\n",
    "        feat_out = mean_feat_var\n",
    "        feat_out += params[\"feat_thresh_scl\"] * tf.sqrt(x=var_feat_var)\n",
    "        with tf.control_dependencies(\n",
    "            control_inputs=[tf.assign(ref=time_anom_thresh_var,\n",
    "                                      value=time_out),\n",
    "                            tf.assign(ref=feat_anom_thresh_var,\n",
    "                                      value=feat_out)]):\n",
    "\n",
    "          loss = tf.reduce_sum(\n",
    "              input_tensor=tf.zeros(shape=(), dtype=tf.float64) * dummy_var)\n",
    "\n",
    "          train_op = tf.contrib.layers.optimize_loss(\n",
    "              loss=loss,\n",
    "              global_step=tf.train.get_global_step(),\n",
    "              learning_rate=params[\"learning_rate\"],\n",
    "              optimizer=\"SGD\")\n",
    "\n",
    "  return loss, train_op\n",
    "\n",
    "\n",
    "def tune_anomaly_thresholds_unsupervised_eval(\n",
    "    cur_batch_size,\n",
    "    time_anom_thresh_var,\n",
    "    mahalanobis_dist_time,\n",
    "    feat_anom_thresh_var,\n",
    "    mahalanobis_dist_feat):\n",
    "  \"\"\"Checks tuned anomaly thresholds during supervised evaluation mode.\n",
    "\n",
    "  Given dimensions of inputs, mahalanobis distances, and variables tracking\n",
    "  counts, means, and variances of mahalanobis distance, returns loss and\n",
    "  train_op.\n",
    "\n",
    "  Args:\n",
    "    cur_batch_size: Current batch size, could be partially filled.\n",
    "    time_anom_thresh_var: Time anomaly threshold variable.\n",
    "    mahalanobis_dist_time: Time major mahalanobis distance.\n",
    "    feat_anom_thresh_var: Feature anomaly threshold variable.\n",
    "    mahalanobis_dist_feat: Feature major mahalanobis distance.\n",
    "\n",
    "  Returns:\n",
    "    loss: The scalar loss to tie our updates back to Estimator graph.\n",
    "    eval_metric_ops: Evaluation metrics of threshold tuning.\n",
    "  \"\"\"\n",
    "  loss = tf.zeros(shape=[], dtype=tf.float64)\n",
    "\n",
    "  # Flag predictions as either normal or anomalous\n",
    "  # shape = (cur_batch_size,)\n",
    "  time_anom_flags = flag_anomalies_by_thresholding(\n",
    "      cur_batch_size, mahalanobis_dist_time, time_anom_thresh_var)\n",
    "\n",
    "  # shape = (cur_batch_size,)\n",
    "  feat_anom_flags = flag_anomalies_by_thresholding(\n",
    "      cur_batch_size, mahalanobis_dist_feat, feat_anom_thresh_var)\n",
    "\n",
    "  # Anomaly detection eval metrics\n",
    "  eval_metric_ops = {\n",
    "      # Time based\n",
    "      \"time_anom_tp\": tf.metrics.mean(values=time_anom_flags),\n",
    "\n",
    "      # Features based\n",
    "      \"feat_anom_tp\": tf.metrics.mean(values=feat_anom_flags)\n",
    "  }\n",
    "\n",
    "  return loss, eval_metric_ops"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## predict.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [],
   "source": [
    "def flag_anomalies_by_thresholding(\n",
    "    cur_batch_size, mahalanobis_dist, anom_thresh_var):\n",
    "  \"\"\"Flags anomalies by thresholding.\n",
    "\n",
    "  Given current batch size, mahalanobis distance, and anomaly threshold\n",
    "  variable, return predicted anomaly flags.\n",
    "\n",
    "  Args:\n",
    "    cur_batch_size: Current batch size, could be partially filled.\n",
    "    mahalanobis_dist: Mahalanobis distance.\n",
    "    anom_thresh_var: Anomaly threshold variable.\n",
    "\n",
    "  Returns:\n",
    "    anomaly_flags: tf.int64 vector of current batch size elements of\n",
    "    0's and 1's indicating if each sequence is anomalous or not.\n",
    "  \"\"\"\n",
    "  anom_flags = tf.where(\n",
    "      condition=tf.reduce_any(\n",
    "          input_tensor=tf.greater(\n",
    "              x=tf.abs(x=mahalanobis_dist),\n",
    "              y=anom_thresh_var),\n",
    "          axis=1),\n",
    "      x=tf.ones(shape=[cur_batch_size], dtype=tf.int64),\n",
    "      y=tf.zeros(shape=[cur_batch_size], dtype=tf.int64))\n",
    "\n",
    "  return anom_flags\n",
    "\n",
    "\n",
    "def anomaly_detection_predictions(\n",
    "    cur_batch_size,\n",
    "    seq_len,\n",
    "    num_feat,\n",
    "    mahalanobis_dist_time,\n",
    "    mahalanobis_dist_feat,\n",
    "    time_anom_thresh_var,\n",
    "    feat_anom_thresh_var,\n",
    "    X_time_abs_recon_err,\n",
    "    X_feat_abs_recon_err):\n",
    "  \"\"\"Creates Estimator predictions and export outputs.\n",
    "\n",
    "  Given dimensions of inputs, mahalanobis distances and their respective\n",
    "  thresholds, and reconstructed inputs' absolute errors, returns Estimator's\n",
    "  predictions and export outputs.\n",
    "\n",
    "  Args:\n",
    "    cur_batch_size: Current batch size, could be partially filled.\n",
    "    seq_len: Number of timesteps in sequence.\n",
    "    num_feat: Number of features.\n",
    "    mahalanobis_dist_time: Mahalanobis distance, time major.\n",
    "    mahalanobis_dist_feat: Mahalanobis distance, features major.\n",
    "    time_anom_thresh_var: Time anomaly threshold variable.\n",
    "    feat_anom_thresh_var: Features anomaly threshold variable.\n",
    "    X_time_abs_recon_err: Time major reconstructed input data's absolute\n",
    "      reconstruction error.\n",
    "    X_feat_abs_recon_err: Features major reconstructed input data's absolute\n",
    "      reconstruction error.\n",
    "\n",
    "  Returns:\n",
    "    predictions_dict: Dictionary of predictions to output for local prediction.\n",
    "    export_outputs: Dictionary to output from exported model for serving.\n",
    "  \"\"\"\n",
    "  # Flag predictions as either normal or anomalous\n",
    "  # shape = (cur_batch_size,)\n",
    "  time_anom_flags = flag_anomalies_by_thresholding(\n",
    "      cur_batch_size, mahalanobis_dist_time, time_anom_thresh_var)\n",
    "\n",
    "  # shape = (cur_batch_size,)\n",
    "  feat_anom_flags = flag_anomalies_by_thresholding(\n",
    "      cur_batch_size, mahalanobis_dist_feat, feat_anom_thresh_var)\n",
    "\n",
    "  # Create predictions dictionary\n",
    "  predictions_dict = {\n",
    "      \"X_time_abs_recon_err\": tf.reshape(\n",
    "          tensor=X_time_abs_recon_err,\n",
    "          shape=[cur_batch_size, seq_len, num_feat]),\n",
    "      \"X_feat_abs_recon_err\": tf.transpose(\n",
    "          a=tf.reshape(\n",
    "              tensor=X_feat_abs_recon_err,\n",
    "              shape=[cur_batch_size, num_feat, seq_len]),\n",
    "          perm=[0, 2, 1]),\n",
    "      \"mahalanobis_dist_time\": mahalanobis_dist_time,\n",
    "      \"mahalanobis_dist_feat\": mahalanobis_dist_feat,\n",
    "      \"time_anom_thresh_var\": tf.fill(\n",
    "          dims=[cur_batch_size], value=time_anom_thresh_var),\n",
    "      \"feat_anom_thresh_var\": tf.fill(\n",
    "          dims=[cur_batch_size], value=feat_anom_thresh_var),\n",
    "      \"time_anom_flags\": time_anom_flags,\n",
    "      \"feat_anom_flags\": feat_anom_flags}\n",
    "\n",
    "  # Create export outputs\n",
    "  export_outputs = {\n",
    "      \"predict_export_outputs\": tf.estimator.export.PredictOutput(\n",
    "          outputs=predictions_dict)\n",
    "  }\n",
    "\n",
    "  return predictions_dict, export_outputs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## anomaly_detection.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create our model function to be used in our custom estimator\n",
    "def anomaly_detection(features, labels, mode, params):\n",
    "  \"\"\"Custom Estimator model function for anomaly detection.\n",
    "\n",
    "  Given dictionary of feature tensors, labels tensor, Estimator mode, and\n",
    "  dictionary for parameters, return EstimatorSpec object for custom Estimator.\n",
    "\n",
    "  Args:\n",
    "    features: Dictionary of feature tensors.\n",
    "    labels: Labels tensor or None.\n",
    "    mode: Estimator ModeKeys. Can take values of TRAIN, EVAL, and PREDICT.\n",
    "    params: Dictionary of parameters.\n",
    "\n",
    "  Returns:\n",
    "    EstimatorSpec object.\n",
    "  \"\"\"\n",
    "  print(\"\\nanomaly_detection: features = \\n{}\".format(features))\n",
    "  print(\"anomaly_detection: labels = \\n{}\".format(labels))\n",
    "  print(\"anomaly_detection: mode = \\n{}\".format(mode))\n",
    "  print(\"anomaly_detection: params = \\n{}\".format(params))\n",
    "\n",
    "  # Get input sequence tensor into correct shape\n",
    "  # Get dynamic batch size in case there was a partially filled batch\n",
    "  cur_batch_size = tf.shape(\n",
    "      input=features[params[\"feat_names\"][0]], out_type=tf.int64)[0]\n",
    "\n",
    "  # Stack all of the features into a 3-D tensor\n",
    "  # shape = (cur_batch_size, seq_len, num_feat)\n",
    "  X = tf.stack(\n",
    "      values=[features[key] for key in params[\"feat_names\"]], axis=2)\n",
    "\n",
    "  ##############################################################################\n",
    "  \n",
    "  # Important to note that flags determining which variables should be created \n",
    "  # need to remain the same through all stages or else they won't be in the\n",
    "  # checkpoint.\n",
    "\n",
    "  # Variables for calculating error distribution statistics\n",
    "  (abs_err_count_time_var,\n",
    "   abs_err_mean_time_var,\n",
    "   abs_err_cov_time_var,\n",
    "   abs_err_inv_cov_time_var,\n",
    "   abs_err_count_feat_var,\n",
    "   abs_err_mean_feat_var,\n",
    "   abs_err_cov_feat_var,\n",
    "   abs_err_inv_cov_feat_var) = create_both_mahalanobis_dist_vars(\n",
    "       seq_len=params[\"seq_len\"], num_feat=params[\"num_feat\"])\n",
    "\n",
    "  # Variables for automatically tuning anomaly thresh\n",
    "  if params[\"labeled_tune_thresh\"]:\n",
    "    (tp_thresh_time_var,\n",
    "     fn_thresh_time_var,\n",
    "     fp_thresh_time_var,\n",
    "     tn_thresh_time_var,\n",
    "     tp_thresh_feat_var,\n",
    "     fn_thresh_feat_var,\n",
    "     fp_thresh_feat_var,\n",
    "     tn_thresh_feat_var) = create_both_confusion_matrix_thresh_vars(\n",
    "         scope=\"mahalanobis_dist_thresh_vars\",\n",
    "         time_thresh_size=[params[\"num_time_anom_thresh\"]],\n",
    "         feat_thresh_size=[params[\"num_feat_anom_thresh\"]])\n",
    "  else:\n",
    "    (count_thresh_time_var,\n",
    "     mean_thresh_time_var,\n",
    "     var_thresh_time_var,\n",
    "     count_thresh_feat_var,\n",
    "     mean_thresh_feat_var,\n",
    "     var_thresh_feat_var) = create_both_mahalanobis_unsupervised_thresh_vars(\n",
    "         scope=\"mahalanobis_dist_thresh_vars\")\n",
    "\n",
    "  with tf.variable_scope(\n",
    "      name_or_scope=\"mahalanobis_dist_thresh_vars\", reuse=tf.AUTO_REUSE):\n",
    "    time_anom_thresh_var = tf.get_variable(\n",
    "        name=\"time_anom_thresh_var\",\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(shape=[], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "    feat_anom_thresh_var = tf.get_variable(\n",
    "        name=\"feat_anom_thresh_var\",\n",
    "        dtype=tf.float64,\n",
    "        initializer=tf.zeros(shape=[], dtype=tf.float64),\n",
    "        trainable=False)\n",
    "\n",
    "  # Variables for tuning anomaly thresh evaluation\n",
    "  if params[\"labeled_tune_thresh\"]:\n",
    "    (tp_thresh_eval_time_var,\n",
    "     fn_thresh_eval_time_var,\n",
    "     fp_thresh_eval_time_var,\n",
    "     tn_thresh_eval_time_var,\n",
    "     tp_thresh_eval_feat_var,\n",
    "     fn_thresh_eval_feat_var,\n",
    "     fp_thresh_eval_feat_var,\n",
    "     tn_thresh_eval_feat_var) = create_both_confusion_matrix_thresh_vars(\n",
    "         scope=\"anom_thresh_eval_vars\",\n",
    "         time_thresh_size=[],\n",
    "         feat_thresh_size=[])\n",
    "\n",
    "  # Create dummy variable for graph dependency requiring a gradient for TRAIN\n",
    "  dummy_var = tf.get_variable(\n",
    "      name=\"dummy_var\",\n",
    "      dtype=tf.float64,\n",
    "      initializer=tf.zeros(shape=[], dtype=tf.float64),\n",
    "      trainable=True)\n",
    "\n",
    "################################################################################\n",
    "\n",
    "  predictions_dict = None\n",
    "  loss = None\n",
    "  train_op = None\n",
    "  eval_metric_ops = None\n",
    "  export_outputs = None\n",
    "\n",
    "  # Now branch off based on which mode we are in\n",
    "\n",
    "  # Call specific model\n",
    "  model_functions = {\n",
    "      \"dense_autoencoder\": dense_autoencoder_model,\n",
    "      \"lstm_enc_dec_autoencoder\": lstm_enc_dec_autoencoder_model,\n",
    "      \"pca\": pca_model}\n",
    "\n",
    "  # Get function pointer for selected model type\n",
    "  model_function = model_functions[params[\"model_type\"]]\n",
    "\n",
    "  # Build selected model\n",
    "  loss, train_op, X_time_orig, X_time_recon, X_feat_orig, X_feat_recon = \\\n",
    "    model_function(X, mode, params, cur_batch_size, dummy_var)\n",
    "\n",
    "  if not (mode == tf.estimator.ModeKeys.TRAIN and\n",
    "          params[\"training_mode\"] == \"reconstruction\"):\n",
    "    # shape = (cur_batch_size * seq_len, num_feat)\n",
    "    X_time_abs_recon_err = tf.abs(\n",
    "        x=X_time_orig - X_time_recon)\n",
    "\n",
    "    # Features based\n",
    "    # shape = (cur_batch_size * num_feat, seq_len)\n",
    "    X_feat_abs_recon_err = tf.abs(\n",
    "        x=X_feat_orig - X_feat_recon)\n",
    "\n",
    "    if (mode == tf.estimator.ModeKeys.TRAIN and\n",
    "        params[\"training_mode\"] == \"calculate_error_distribution_statistics\"):\n",
    "      loss, train_op = calculate_error_distribution_statistics_training(\n",
    "          cur_batch_size,\n",
    "          X_time_abs_recon_err,\n",
    "          abs_err_count_time_var,\n",
    "          abs_err_mean_time_var,\n",
    "          abs_err_cov_time_var,\n",
    "          abs_err_inv_cov_time_var,\n",
    "          X_feat_abs_recon_err,\n",
    "          abs_err_count_feat_var,\n",
    "          abs_err_mean_feat_var,\n",
    "          abs_err_cov_feat_var,\n",
    "          abs_err_inv_cov_feat_var,\n",
    "          params,\n",
    "          dummy_var)\n",
    "    elif (mode == tf.estimator.ModeKeys.EVAL and\n",
    "          params[\"training_mode\"] != \"tune_anomaly_thresholds\"):\n",
    "      loss, eval_metric_ops = reconstruction_evaluation(\n",
    "          X_time_orig, X_time_recon, params[\"training_mode\"])\n",
    "    elif (mode == tf.estimator.ModeKeys.PREDICT or\n",
    "          ((mode == tf.estimator.ModeKeys.TRAIN or\n",
    "            mode == tf.estimator.ModeKeys.EVAL) and\n",
    "           params[\"training_mode\"] == \"tune_anomaly_thresholds\")):\n",
    "      with tf.variable_scope(\n",
    "          name_or_scope=\"mahalanobis_dist_vars\", reuse=tf.AUTO_REUSE):\n",
    "        # Time based\n",
    "        # shape = (cur_batch_size, seq_len)\n",
    "        mahalanobis_dist_time = mahalanobis_dist(\n",
    "            err_vec=X_time_abs_recon_err,\n",
    "            mean_vec=abs_err_mean_time_var,\n",
    "            inv_cov=abs_err_inv_cov_time_var,\n",
    "            final_shape=params[\"seq_len\"])\n",
    "\n",
    "        # Features based\n",
    "        # shape = (cur_batch_size, num_feat)\n",
    "        mahalanobis_dist_feat = mahalanobis_dist(\n",
    "            err_vec=X_feat_abs_recon_err,\n",
    "            mean_vec=abs_err_mean_feat_var,\n",
    "            inv_cov=abs_err_inv_cov_feat_var,\n",
    "            final_shape=params[\"num_feat\"])\n",
    "\n",
    "      if mode != tf.estimator.ModeKeys.PREDICT:\n",
    "        if params[\"labeled_tune_thresh\"]:\n",
    "          labels_norm_mask = tf.equal(x=labels, y=0)\n",
    "          labels_anom_mask = tf.equal(x=labels, y=1)\n",
    "\n",
    "          if mode == tf.estimator.ModeKeys.TRAIN:\n",
    "            loss, train_op = tune_anomaly_thresholds_supervised_training(\n",
    "                labels_norm_mask,\n",
    "                labels_anom_mask,\n",
    "                mahalanobis_dist_time,\n",
    "                tp_thresh_time_var,\n",
    "                fn_thresh_time_var,\n",
    "                fp_thresh_time_var,\n",
    "                tn_thresh_time_var,\n",
    "                time_anom_thresh_var,\n",
    "                mahalanobis_dist_feat,\n",
    "                tp_thresh_feat_var,\n",
    "                fn_thresh_feat_var,\n",
    "                fp_thresh_feat_var,\n",
    "                tn_thresh_feat_var,\n",
    "                feat_anom_thresh_var,\n",
    "                params,\n",
    "                mode,\n",
    "                dummy_var)\n",
    "          elif mode == tf.estimator.ModeKeys.EVAL:\n",
    "            loss, eval_metric_ops = tune_anomaly_thresholds_supervised_eval(\n",
    "                labels_norm_mask,\n",
    "                labels_anom_mask,\n",
    "                time_anom_thresh_var,\n",
    "                mahalanobis_dist_time,\n",
    "                tp_thresh_eval_time_var,\n",
    "                fn_thresh_eval_time_var,\n",
    "                fp_thresh_eval_time_var,\n",
    "                tn_thresh_eval_time_var,\n",
    "                feat_anom_thresh_var,\n",
    "                mahalanobis_dist_feat,\n",
    "                tp_thresh_eval_feat_var,\n",
    "                fn_thresh_eval_feat_var,\n",
    "                fp_thresh_eval_feat_var,\n",
    "                tn_thresh_eval_feat_var,\n",
    "                params,\n",
    "                mode)\n",
    "        else:  # not params[\"labeled_tune_thresh\"]\n",
    "          if mode == tf.estimator.ModeKeys.TRAIN:\n",
    "            loss, train_op = tune_anomaly_thresholds_unsupervised_training(\n",
    "                cur_batch_size,\n",
    "                time_anom_thresh_var,\n",
    "                mahalanobis_dist_time,\n",
    "                count_thresh_time_var,\n",
    "                mean_thresh_time_var,\n",
    "                var_thresh_time_var,\n",
    "                feat_anom_thresh_var,\n",
    "                mahalanobis_dist_feat,\n",
    "                count_thresh_feat_var,\n",
    "                mean_thresh_feat_var,\n",
    "                var_thresh_feat_var,\n",
    "                params,\n",
    "                dummy_var)\n",
    "          elif mode == tf.estimator.ModeKeys.EVAL:\n",
    "            loss, eval_metric_ops = tune_anomaly_thresholds_unsupervised_eval(\n",
    "                cur_batch_size,\n",
    "                time_anom_thresh_var,\n",
    "                mahalanobis_dist_time,\n",
    "                feat_anom_thresh_var,\n",
    "                mahalanobis_dist_feat)\n",
    "      else:  # mode == tf.estimator.ModeKeys.PREDICT\n",
    "        predictions_dict, export_outputs = anomaly_detection_predictions(\n",
    "            cur_batch_size,\n",
    "            params[\"seq_len\"],\n",
    "            params[\"num_feat\"],\n",
    "            mahalanobis_dist_time,\n",
    "            mahalanobis_dist_feat,\n",
    "            time_anom_thresh_var,\n",
    "            feat_anom_thresh_var,\n",
    "            X_time_abs_recon_err,\n",
    "            X_feat_abs_recon_err)\n",
    "\n",
    "  # Return EstimatorSpec\n",
    "  return tf.estimator.EstimatorSpec(\n",
    "      mode=mode,\n",
    "      predictions=predictions_dict,\n",
    "      loss=loss,\n",
    "      train_op=train_op,\n",
    "      eval_metric_ops=eval_metric_ops,\n",
    "      export_outputs=export_outputs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## serving.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Serving input functions\n",
    "def fix_shape_and_type_for_serving(placeholder):\n",
    "  \"\"\"Fixes the shape and type of serving input strings.\n",
    "\n",
    "  Given placeholder tensor, return parsed and processed feature tensor.\n",
    "\n",
    "  Args:\n",
    "    placeholder: Placeholder tensor holding raw data from serving input\n",
    "      function.\n",
    "\n",
    "  Returns:\n",
    "    Parsed and processed feature tensor.\n",
    "  \"\"\"\n",
    "  cur_batch_size = tf.shape(input=placeholder, out_type=tf.int64)[0]\n",
    "\n",
    "  # String split each string in batch and output values from the resulting\n",
    "  # SparseTensors\n",
    "  # shape = (batch_size, seq_len)\n",
    "  split_string = tf.stack(values=tf.map_fn(\n",
    "      fn=lambda x: tf.string_split(\n",
    "          source=[placeholder[x]], delimiter=\";\").values,\n",
    "      elems=tf.range(\n",
    "          start=0, limit=cur_batch_size, dtype=tf.int64),\n",
    "      dtype=tf.string), axis=0)\n",
    "\n",
    "  # Convert each string in the split tensor to float\n",
    "  # shape = (batch_size, seq_len)\n",
    "  feature_tensor = tf.string_to_number(\n",
    "      string_tensor=split_string, out_type=tf.float64)\n",
    "\n",
    "  return feature_tensor\n",
    "\n",
    "\n",
    "def get_shape_and_set_modified_shape_2D(tensor, additional_dimension_sizes):\n",
    "  \"\"\"Fixes the shape and type of serving input strings.\n",
    "\n",
    "  Given feature tensor and additional dimension size, sequence length,\n",
    "  fixes dynamic shape ambiguity of last dimension so that we will be able to\n",
    "  use it in our DNN (since tf.layers.dense require the last dimension to be\n",
    "  known).\n",
    "\n",
    "  Args:\n",
    "    tensor: tf.float64 vector feature tensor.\n",
    "    additional_dimension_sizes: Additional dimension size, namely sequence\n",
    "      length.\n",
    "\n",
    "  Returns:\n",
    "    Feature tensor with set static shape for sequence length.\n",
    "  \"\"\"\n",
    "  # Get static shape for tensor and convert it to list\n",
    "  shape = tensor.get_shape().as_list()\n",
    "  # Set outer shape to additional_dimension_sizes[0] since know this is the\n",
    "  # correct size\n",
    "  shape[1] = additional_dimension_sizes[0]\n",
    "  # Set the shape of tensor to our modified shape\n",
    "  # shape = (batch_size, additional_dimension_sizes[0])\n",
    "  tensor.set_shape(shape=shape)\n",
    "\n",
    "  return tensor\n",
    "\n",
    "\n",
    "def serving_input_fn(feat_names, seq_len):\n",
    "  \"\"\"Serving input function.\n",
    "\n",
    "  Given the sequence length, return ServingInputReceiver object.\n",
    "\n",
    "  Args:\n",
    "    feat_names: List of string names of features.\n",
    "    seq_len: Number of timesteps in sequence.\n",
    "\n",
    "  Returns:\n",
    "    ServingInputReceiver object containing features and receiver tensors.\n",
    "  \"\"\"\n",
    "  # Create placeholders to accept the data sent to the model at serving time\n",
    "  # All features come in as a batch of strings, shape = (batch_size,),\n",
    "  # this was so because of passing the arrays to online ml-engine prediction\n",
    "  feature_placeholders = {\n",
    "      feature: tf.placeholder(\n",
    "          dtype=tf.string, shape=[None])\n",
    "      for feature in feat_names\n",
    "  }\n",
    "\n",
    "  # Create feature tensors\n",
    "  features = {key: fix_shape_and_type_for_serving(placeholder=tensor)\n",
    "              for key, tensor in feature_placeholders.items()}\n",
    "\n",
    "  # Fix dynamic shape ambiguity of feature tensors for our DNN\n",
    "  features = {key: get_shape_and_set_modified_shape_2D(\n",
    "      tensor=tensor, additional_dimension_sizes=[seq_len])\n",
    "              for key, tensor in features.items()}\n",
    "\n",
    "  return tf.estimator.export.ServingInputReceiver(\n",
    "      features=features, receiver_tensors=feature_placeholders)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## model.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set logging to be level of INFO\n",
    "tf.logging.set_verbosity(tf.logging.INFO)\n",
    "\n",
    "\n",
    "def train_and_evaluate(args):\n",
    "  \"\"\"Train and evaluate custom Estimator with three training modes.\n",
    "\n",
    "  Given the dictionary of parameters, create custom Estimator and run up to\n",
    "  three training modes then return Estimator object.\n",
    "\n",
    "  Args:\n",
    "    args: Dictionary of parameters.\n",
    "\n",
    "  Returns:\n",
    "    Estimator object.\n",
    "  \"\"\"\n",
    "  # Create our custom estimator using our model function\n",
    "  estimator = tf.estimator.Estimator(\n",
    "      model_fn=anomaly_detection,\n",
    "      model_dir=args[\"output_dir\"],\n",
    "      params={key: val for key, val in args.items()})\n",
    "\n",
    "  if args[\"training_mode\"] == \"reconstruction\":\n",
    "    # Calculate max_steps\n",
    "    max_steps = int(args[\"reconstruction_epochs\"] * args[\"train_examples\"])\n",
    "    max_steps = max_steps // args[\"train_batch_size\"]\n",
    "    max_steps += args[\"previous_train_steps\"]\n",
    "    \n",
    "    # Create eval spec to read in our validation data\n",
    "    eval_spec = tf.estimator.EvalSpec(\n",
    "        input_fn=read_dataset(\n",
    "            filename=args[\"eval_file_pattern\"],\n",
    "            mode=tf.estimator.ModeKeys.EVAL,\n",
    "            batch_size=args[\"eval_batch_size\"],\n",
    "            params=args),\n",
    "        steps=None,\n",
    "        start_delay_secs=args[\"start_delay_secs\"],  # start eval after N secs\n",
    "        throttle_secs=args[\"throttle_secs\"])  # evaluate every N secs\n",
    "\n",
    "    if args[\"model_type\"] == \"pca\":\n",
    "      # Create train spec to read in our training data\n",
    "      train_spec = tf.estimator.TrainSpec(\n",
    "          input_fn=read_dataset(\n",
    "              filename=args[\"train_file_pattern\"],\n",
    "              mode=tf.estimator.ModeKeys.EVAL,  # read through train data once\n",
    "              batch_size=args[\"train_batch_size\"],\n",
    "              params=args),\n",
    "          max_steps=max_steps)\n",
    "      # Check to see if we need to additionally tune principal components\n",
    "      if not args[\"autotune_principal_components\"]:\n",
    "        # Create train and evaluate loop to train and evaluate our estimator\n",
    "        tf.estimator.train_and_evaluate(\n",
    "            estimator=estimator, train_spec=train_spec, eval_spec=eval_spec)\n",
    "      else:\n",
    "          if (args[\"k_principal_components_time\"] is None or\n",
    "              args[\"k_principal_components_feat\"] is None):\n",
    "            # Create train and evaluate loop to train and evaluate our estimator\n",
    "            tf.estimator.train_and_evaluate(\n",
    "                estimator=estimator, train_spec=train_spec, eval_spec=eval_spec)\n",
    "    else:  # dense_autoencoder or lstm_enc_dec_autoencoder\n",
    "      # Create early stopping hook to help reduce overfitting\n",
    "      early_stopping_hook = tf.contrib.estimator.stop_if_no_decrease_hook(\n",
    "          estimator=estimator,\n",
    "          metric_name=\"rmse\",\n",
    "          max_steps_without_decrease=100,\n",
    "          min_steps=1000,\n",
    "          run_every_secs=60,\n",
    "          run_every_steps=None)\n",
    "\n",
    "      # Create train spec to read in our training data\n",
    "      train_spec = tf.estimator.TrainSpec(\n",
    "          input_fn=read_dataset(\n",
    "              filename=args[\"train_file_pattern\"],\n",
    "              mode=tf.estimator.ModeKeys.TRAIN,\n",
    "              batch_size=args[\"train_batch_size\"],\n",
    "              params=args),\n",
    "          max_steps=max_steps,\n",
    "          hooks=[early_stopping_hook])\n",
    "\n",
    "      # Create train and evaluate loop to train and evaluate our estimator\n",
    "      tf.estimator.train_and_evaluate(\n",
    "          estimator=estimator, train_spec=train_spec, eval_spec=eval_spec)\n",
    "  else:\n",
    "    # Calculate max_steps\n",
    "    max_steps = args[\"train_examples\"] // args[\"train_batch_size\"]\n",
    "    max_steps += args[\"previous_train_steps\"]\n",
    "\n",
    "    # if args[\"training_mode\"] == \"calculate_error_distribution_statistics\"\n",
    "    # Get final mahalanobis statistics over the entire val_1 dataset\n",
    "\n",
    "    # if args[\"training_mode\"] == \"tune_anomaly_thresholds\"\n",
    "    # Tune anomaly thresholds using val_2 and val_anom datasets\n",
    "    train_spec = tf.estimator.TrainSpec(\n",
    "        input_fn=read_dataset(\n",
    "            filename=args[\"train_file_pattern\"],\n",
    "            mode=tf.estimator.ModeKeys.EVAL,  # read through val data once\n",
    "            batch_size=args[\"train_batch_size\"],\n",
    "            params=args),\n",
    "        max_steps=max_steps)\n",
    "\n",
    "    if args[\"training_mode\"] == \"calculate_error_distribution_statistics\":\n",
    "      # Don't create exporter for serving yet since anomaly thresholds\n",
    "      # aren't trained yet\n",
    "      exporter = None\n",
    "    elif args[\"training_mode\"] == \"tune_anomaly_thresholds\":\n",
    "      # Create exporter that uses serving_input_fn to create saved_model\n",
    "      # for serving\n",
    "      exporter = tf.estimator.LatestExporter(\n",
    "          name=\"exporter\",\n",
    "          serving_input_receiver_fn=lambda: serving_input_fn(\n",
    "              args[\"feat_names\"], args[\"seq_len\"]))\n",
    "    else:\n",
    "      print(\"{0} isn't a valid training mode!\".format(args[\"training_mode\"]))\n",
    "\n",
    "    # Create eval spec to read in our validation data and export our model\n",
    "    eval_spec = tf.estimator.EvalSpec(\n",
    "        input_fn=read_dataset(\n",
    "            filename=args[\"eval_file_pattern\"],\n",
    "            mode=tf.estimator.ModeKeys.EVAL,\n",
    "            batch_size=args[\"eval_batch_size\"],\n",
    "            params=args),\n",
    "        steps=None,\n",
    "        exporters=exporter,\n",
    "        start_delay_secs=args[\"start_delay_secs\"],  # start eval after N secs\n",
    "        throttle_secs=args[\"throttle_secs\"])  # evaluate every N secs\n",
    "\n",
    "  if (args[\"training_mode\"] == \"calculate_error_distribution_statistics\" or\n",
    "      args[\"training_mode\"] == \"tune_anomaly_thresholds\"):\n",
    "    # Create train and evaluate loop to train and evaluate our estimator\n",
    "    tf.estimator.train_and_evaluate(\n",
    "        estimator=estimator, train_spec=train_spec, eval_spec=eval_spec)\n",
    "\n",
    "  return estimator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [],
   "source": [
    "arguments = {}\n",
    "# File arguments\n",
    "arguments[\"train_file_pattern\"] = \"data/train_norm_seq.csv\"\n",
    "arguments[\"eval_file_pattern\"] = \"data/val_norm_1_seq.csv\"\n",
    "arguments[\"output_dir\"] = \"trained_model\"\n",
    "\n",
    "# Sequence shape hyperparameters\n",
    "arguments[\"seq_len\"] = seq_len\n",
    "arguments[\"num_feat\"] = len(tag_columns)\n",
    "\n",
    "# Feature hyperparameters\n",
    "arguments[\"feat_names\"] = tag_columns\n",
    "arguments[\"feat_defaults\"] = [[(\";\").join([\"0.0\"] * arguments[\"seq_len\"])]] * arguments[\"num_feat\"]\n",
    "\n",
    "# Training parameters\n",
    "arguments[\"train_batch_size\"] = 32\n",
    "arguments[\"eval_batch_size\"] = 32\n",
    "arguments[\"previous_train_steps\"] = 0\n",
    "arguments[\"reconstruction_epochs\"] = 1.0\n",
    "arguments[\"train_examples\"] = 64000\n",
    "arguments[\"eval_examples\"] = 6400\n",
    "arguments[\"learning_rate\"] = 0.01\n",
    "arguments[\"start_delay_secs\"] = 60\n",
    "arguments[\"throttle_secs\"] = 120\n",
    "\n",
    "# Model parameters\n",
    "# [dense_autoencoder, lstm_enc_dec_autoencoder, pca]\n",
    "# arguments[\"model_type\"] = \"lstm_enc_dec_autoencoder\"\n",
    "arguments[\"model_type\"] = \"pca\"\n",
    "\n",
    "## Dense Autoencoder\n",
    "arguments[\"enc_dnn_hidden_units\"] = [64, 32, 16]\n",
    "arguments[\"latent_vector_size\"] = min(8, arguments[\"enc_dnn_hidden_units\"][-1])\n",
    "arguments[\"dec_dnn_hidden_units\"] = [16, 32, 64]\n",
    "arguments[\"time_loss_weight\"] = 1.0\n",
    "arguments[\"feat_loss_weight\"] = 1.0\n",
    "\n",
    "## LSTM Encoder-Decoder Autoencoder\n",
    "arguments[\"reverse_labels_sequence\"] = True\n",
    "### LSTM hyperparameters\n",
    "arguments[\"enc_lstm_hidden_units\"] = [64, 32, 16]\n",
    "arguments[\"dec_lstm_hidden_units\"] = [16, 32, 64]\n",
    "arguments[\"lstm_dropout_output_keep_probs\"] = [1.0, 1.0, 1.0]\n",
    "### DNN hyperparameters\n",
    "arguments[\"dnn_hidden_units\"] = [1024, 256, 64]\n",
    "\n",
    "## PCA\n",
    "arguments[\"autotune_principal_components\"] = False\n",
    "arguments[\"k_principal_components_time\"] = None\n",
    "arguments[\"k_principal_components_feat\"] = None\n",
    "\n",
    "# Anomaly detection\n",
    "arguments[\"training_mode\"] = \"reconstruction\"\n",
    "arguments[\"labeled_tune_thresh\"] = True\n",
    "arguments[\"num_time_anom_thresh\"] = 300\n",
    "arguments[\"num_feat_anom_thresh\"] = 300\n",
    "arguments[\"min_time_anom_thresh\"] = 1\n",
    "arguments[\"max_time_anom_thresh\"] = 100\n",
    "arguments[\"min_feat_anom_thresh\"] = 1\n",
    "arguments[\"max_feat_anom_thresh\"] = 100\n",
    "arguments[\"time_thresh_scl\"] = 2.0\n",
    "arguments[\"feat_thresh_scl\"] = 2.0\n",
    "arguments[\"time_anom_thresh\"] = None\n",
    "arguments[\"feat_anom_thresh\"] = None\n",
    "arguments[\"eps\"] = 10**-12\n",
    "arguments[\"f_score_beta\"] = 0.05"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train reconstruction variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Using default config.\n",
      "INFO:tensorflow:Using config: {'_device_fn': None, '_num_worker_replicas': 1, '_service': None, '_tf_random_seed': None, '_save_checkpoints_secs': 600, '_is_chief': True, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_experimental_distribute': None, '_keep_checkpoint_every_n_hours': 10000, '_global_id_in_cluster': 0, '_protocol': None, '_keep_checkpoint_max': 5, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f97dc87e978>, '_eval_distribute': None, '_num_ps_replicas': 0, '_task_id': 0, '_train_distribute': None, '_task_type': 'worker', '_master': '', '_session_config': allow_soft_placement: true\n",
      "graph_options {\n",
      "  rewrite_options {\n",
      "    meta_optimizer_iterations: ONE\n",
      "  }\n",
      "}\n",
      ", '_log_step_count_steps': 100, '_model_dir': 'trained_model', '_evaluation_master': ''}\n",
      "INFO:tensorflow:Not using Distribute Coordinator.\n",
      "INFO:tensorflow:Running training and evaluation locally (non-distributed).\n",
      "INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n",
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'IteratorGetNext:2' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'IteratorGetNext:0' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'IteratorGetNext:1' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'IteratorGetNext:4' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'IteratorGetNext:3' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "None\n",
      "anomaly_detection: mode = \n",
      "train\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 64000, 'k_principal_components_time': None, 'throttle_secs': 120, 'min_feat_anom_thresh': 1, 'min_time_anom_thresh': 1, 'train_batch_size': 32, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 0, 'output_dir': 'trained_model', 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/train_norm_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'num_feat_anom_thresh': 300, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/val_norm_1_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'reconstruction', 'eval_batch_size': 32, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "value_b = \n",
      "Tensor(\"Reshape:0\", shape=(?, 5), dtype=float64)\n",
      "mean_a = \n",
      "<tf.Variable 'pca_vars/pca_time_mean_var:0' shape=(5,) dtype=float64_ref>\n",
      "mean_ab = \n",
      "Tensor(\"pca_vars_2/cond/truediv:0\", shape=(5,), dtype=float64)\n",
      "value_b = \n",
      "Tensor(\"Reshape_1:0\", shape=(?, 30), dtype=float64)\n",
      "mean_a = \n",
      "<tf.Variable 'pca_vars/pca_feat_mean_var:0' shape=(30,) dtype=float64_ref>\n",
      "mean_ab = \n",
      "Tensor(\"pca_vars_2/cond_1/truediv:0\", shape=(30,), dtype=float64)\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "INFO:tensorflow:Create CheckpointSaverHook.\n",
      "INFO:tensorflow:Graph was finalized.\n",
      "INFO:tensorflow:Running local_init_op.\n",
      "INFO:tensorflow:Done running local_init_op.\n",
      "INFO:tensorflow:Saving checkpoints for 0 into trained_model/model.ckpt.\n",
      "INFO:tensorflow:loss = 0.0, step = 1\n",
      "INFO:tensorflow:global_step/sec: 114.147\n",
      "INFO:tensorflow:loss = 0.0, step = 101 (0.878 sec)\n",
      "INFO:tensorflow:global_step/sec: 127.446\n",
      "INFO:tensorflow:loss = 0.0, step = 201 (0.784 sec)\n",
      "INFO:tensorflow:global_step/sec: 102.852\n",
      "INFO:tensorflow:loss = 0.0, step = 301 (0.973 sec)\n",
      "INFO:tensorflow:global_step/sec: 127.028\n",
      "INFO:tensorflow:loss = 0.0, step = 401 (0.787 sec)\n",
      "INFO:tensorflow:global_step/sec: 147.752\n",
      "INFO:tensorflow:loss = 0.0, step = 501 (0.677 sec)\n",
      "INFO:tensorflow:global_step/sec: 209.312\n",
      "INFO:tensorflow:loss = 0.0, step = 601 (0.478 sec)\n",
      "INFO:tensorflow:global_step/sec: 203.779\n",
      "INFO:tensorflow:loss = 0.0, step = 701 (0.491 sec)\n",
      "INFO:tensorflow:global_step/sec: 209.846\n",
      "INFO:tensorflow:loss = 0.0, step = 801 (0.477 sec)\n",
      "INFO:tensorflow:global_step/sec: 205.316\n",
      "INFO:tensorflow:loss = 0.0, step = 901 (0.487 sec)\n",
      "INFO:tensorflow:global_step/sec: 207.866\n",
      "INFO:tensorflow:loss = 0.0, step = 1001 (0.481 sec)\n",
      "INFO:tensorflow:global_step/sec: 206.468\n",
      "INFO:tensorflow:loss = 0.0, step = 1101 (0.484 sec)\n",
      "INFO:tensorflow:global_step/sec: 209.834\n",
      "INFO:tensorflow:loss = 0.0, step = 1201 (0.477 sec)\n",
      "INFO:tensorflow:global_step/sec: 202.18\n",
      "INFO:tensorflow:loss = 0.0, step = 1301 (0.495 sec)\n",
      "INFO:tensorflow:global_step/sec: 205.017\n",
      "INFO:tensorflow:loss = 0.0, step = 1401 (0.487 sec)\n",
      "INFO:tensorflow:global_step/sec: 207.157\n",
      "INFO:tensorflow:loss = 0.0, step = 1501 (0.483 sec)\n",
      "INFO:tensorflow:global_step/sec: 207.536\n",
      "INFO:tensorflow:loss = 0.0, step = 1601 (0.482 sec)\n",
      "INFO:tensorflow:global_step/sec: 206.412\n",
      "INFO:tensorflow:loss = 0.0, step = 1701 (0.484 sec)\n",
      "INFO:tensorflow:global_step/sec: 205.964\n",
      "INFO:tensorflow:loss = 0.0, step = 1801 (0.486 sec)\n",
      "INFO:tensorflow:global_step/sec: 209.512\n",
      "INFO:tensorflow:loss = 0.0, step = 1901 (0.477 sec)\n",
      "INFO:tensorflow:Saving checkpoints for 2000 into trained_model/model.ckpt.\n",
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'IteratorGetNext:2' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'IteratorGetNext:0' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'IteratorGetNext:1' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'IteratorGetNext:4' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'IteratorGetNext:3' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "None\n",
      "anomaly_detection: mode = \n",
      "eval\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 64000, 'k_principal_components_time': None, 'throttle_secs': 120, 'min_feat_anom_thresh': 1, 'min_time_anom_thresh': 1, 'train_batch_size': 32, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 0, 'output_dir': 'trained_model', 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/train_norm_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'num_feat_anom_thresh': 300, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/val_norm_1_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'reconstruction', 'eval_batch_size': 32, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "INFO:tensorflow:Starting evaluation at 2019-07-16T19:44:41Z\n",
      "INFO:tensorflow:Graph was finalized.\n",
      "INFO:tensorflow:Restoring parameters from trained_model/model.ckpt-2000\n",
      "INFO:tensorflow:Running local_init_op.\n",
      "INFO:tensorflow:Done running local_init_op.\n",
      "INFO:tensorflow:Finished evaluation at 2019-07-16-19:44:42\n",
      "INFO:tensorflow:Saving dict for global step 2000: global_step = 2000, loss = 0.9952365, mae = 0.836643, rmse = 0.99761575\n",
      "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 2000: trained_model/model.ckpt-2000\n",
      "INFO:tensorflow:Loss for final step: 0.0.\n"
     ]
    }
   ],
   "source": [
    "# Train the model\n",
    "arguments[\"training_mode\"] = \"reconstruction\"\n",
    "arguments[\"train_file_pattern\"] = \"data/train_norm_seq.csv\"\n",
    "arguments[\"eval_file_pattern\"] = \"data/val_norm_1_seq.csv\"\n",
    "arguments[\"train_batch_size\"] = 32\n",
    "arguments[\"eval_batch_size\"] = 32\n",
    "arguments[\"previous_train_steps\"] = 0\n",
    "arguments[\"reconstruction_epochs\"] = 1.0\n",
    "arguments[\"train_examples\"] = 64000\n",
    "arguments[\"eval_examples\"] = 6400\n",
    "shutil.rmtree(\n",
    "    path=arguments[\"output_dir\"], ignore_errors=True)  # start fresh each time\n",
    "estimator = train_and_evaluate(arguments)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Look at any special variable values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['OptimizeLoss/learning_rate',\n",
       " 'anom_thresh_eval_vars/fn_thresh_feat_var',\n",
       " 'anom_thresh_eval_vars/fn_thresh_time_var',\n",
       " 'anom_thresh_eval_vars/fp_thresh_feat_var',\n",
       " 'anom_thresh_eval_vars/fp_thresh_time_var',\n",
       " 'anom_thresh_eval_vars/tn_thresh_feat_var',\n",
       " 'anom_thresh_eval_vars/tn_thresh_time_var',\n",
       " 'anom_thresh_eval_vars/tp_thresh_feat_var',\n",
       " 'anom_thresh_eval_vars/tp_thresh_time_var',\n",
       " 'dummy_var',\n",
       " 'global_step',\n",
       " 'mahalanobis_dist_thresh_vars/feat_anom_thresh_var',\n",
       " 'mahalanobis_dist_thresh_vars/fn_thresh_feat_var',\n",
       " 'mahalanobis_dist_thresh_vars/fn_thresh_time_var',\n",
       " 'mahalanobis_dist_thresh_vars/fp_thresh_feat_var',\n",
       " 'mahalanobis_dist_thresh_vars/fp_thresh_time_var',\n",
       " 'mahalanobis_dist_thresh_vars/time_anom_thresh_var',\n",
       " 'mahalanobis_dist_thresh_vars/tn_thresh_feat_var',\n",
       " 'mahalanobis_dist_thresh_vars/tn_thresh_time_var',\n",
       " 'mahalanobis_dist_thresh_vars/tp_thresh_feat_var',\n",
       " 'mahalanobis_dist_thresh_vars/tp_thresh_time_var',\n",
       " 'mahalanobis_dist_vars/abs_err_count_feat_var',\n",
       " 'mahalanobis_dist_vars/abs_err_count_time_var',\n",
       " 'mahalanobis_dist_vars/abs_err_cov_feat_var',\n",
       " 'mahalanobis_dist_vars/abs_err_cov_time_var',\n",
       " 'mahalanobis_dist_vars/abs_err_inv_cov_feat_var',\n",
       " 'mahalanobis_dist_vars/abs_err_inv_cov_time_var',\n",
       " 'mahalanobis_dist_vars/abs_err_mean_feat_var',\n",
       " 'mahalanobis_dist_vars/abs_err_mean_time_var',\n",
       " 'pca_vars/pca_feat_count_var',\n",
       " 'pca_vars/pca_feat_cov_var',\n",
       " 'pca_vars/pca_feat_eigval_var',\n",
       " 'pca_vars/pca_feat_eigvec_var',\n",
       " 'pca_vars/pca_feat_k_principal_components_var',\n",
       " 'pca_vars/pca_feat_mean_var',\n",
       " 'pca_vars/pca_time_count_var',\n",
       " 'pca_vars/pca_time_cov_var',\n",
       " 'pca_vars/pca_time_eigval_var',\n",
       " 'pca_vars/pca_time_eigvec_var',\n",
       " 'pca_vars/pca_time_k_principal_components_var',\n",
       " 'pca_vars/pca_time_mean_var']"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "estimator.get_variable_names()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_pca_variables(X, dim_name):\n",
    "  \"\"\"Print PCA variables.\n",
    "\n",
    "  Given the dictionary of parameters, create custom Estimator and run up to\n",
    "  three training modes then return Estimator object.\n",
    "\n",
    "  Args:\n",
    "    X: np.float64 2D array of feature data.\n",
    "    dim_name: Name of dimension, values can be time or feat.\n",
    "  \"\"\"\n",
    "  print(\"X_{}.shape = \\n{}\".format(dim_name, X.shape))\n",
    "\n",
    "  # Count\n",
    "  count_var = estimator.get_variable_value(\n",
    "      name=\"pca_vars/pca_{}_count_var\".format(dim_name))\n",
    "  print(\"{}_count_var = \\n{}\".format(dim_name, count_var))\n",
    "  count = X.shape[0]\n",
    "  print(\"{}_count = \\n{}\".format(dim_name, count))\n",
    "\n",
    "  # Mean\n",
    "  mean_var = estimator.get_variable_value(\n",
    "      name=\"pca_vars/pca_{}_mean_var\".format(dim_name))\n",
    "  print(\"{}_mean_var = \\n{}\".format(dim_name, mean_var))\n",
    "  mean = np.mean(X, axis=0)\n",
    "  print(\"{}_mean = \\n{}\".format(dim_name, mean))\n",
    "  print(\"{}_mean_ratio = \\n{}\".format(dim_name, mean_var / mean))\n",
    "\n",
    "  # Covariance\n",
    "  cov_var = estimator.get_variable_value(\n",
    "      name=\"pca_vars/pca_{}_cov_var\".format(dim_name))\n",
    "  if cov_var.shape[0] <= 10:\n",
    "    print(\"{}_cov_var = \\n{}\".format(dim_name, cov_var))\n",
    "  else:\n",
    "    print(\"{}_cov_var.shape = \\n{}\".format(dim_name, cov_var.shape))\n",
    "\n",
    "  if arguments[\"seq_len\"] == 1:\n",
    "    cov = np.zeros(shape=[num_tags, num_tags])\n",
    "  else:\n",
    "    cov = np.cov(m=np.transpose(a=X))\n",
    "\n",
    "  if cov.shape[0] <= 10:\n",
    "    print(\"{}_cov = \\n{}\".format(dim_name, cov))\n",
    "  else:\n",
    "    print(\"{}_cov.shape = \\n{}\".format(dim_name, cov.shape))\n",
    "\n",
    "  print(\"{}_cov_ratio = \\n{}\".format(dim_name, cov_var / cov))\n",
    "\n",
    "  # Eigenvalues\n",
    "  eigval_var = estimator.get_variable_value(\n",
    "      name=\"pca_vars/pca_{}_eigval_var\".format(dim_name))\n",
    "  if eigval_var.shape[0] <= 10:\n",
    "    print(\"{}_eigval_var = \\n{}\".format(dim_name, eigval_var))\n",
    "  else:\n",
    "    print(\"{}_eigval_var.shape = \\n{}\".format(dim_name, eigval_var.shape))\n",
    "\n",
    "  eigval, eigvec = np.linalg.eigh(a=cov)\n",
    "  if eigval.shape[0] <= 10:\n",
    "    print(\"{}_eigval = \\n{}\".format(dim_name, eigval))\n",
    "  else:\n",
    "    print(\"{}_eigval.shape = \\n{}\".format(dim_name, eigval.shape))\n",
    "\n",
    "  print(\"{}_eigval_ratio = \\n{}\".format(dim_name, eigval_var / eigval))\n",
    "\n",
    "  # Eigenvectors\n",
    "  eigvec_var = estimator.get_variable_value(\n",
    "      name=\"pca_vars/pca_{}_eigvec_var\".format(dim_name))\n",
    "  if eigvec_var.shape[0] <= 10:\n",
    "    print(\"{}_eigvec_var = \\n{}\".format(dim_name, eigvec_var))\n",
    "  else:\n",
    "    print(\"{}_eigvec_var.shape = \\n{}\".format(dim_name, eigvec_var.shape))\n",
    "\n",
    "  if eigvec.shape[0] <= 10:\n",
    "    print(\"{}_eigvec = \\n{}\".format(dim_name, eigvec))\n",
    "  else:\n",
    "    print(\"{}_eigvec.shape = \\n{}\".format(dim_name, eigvec.shape))\n",
    "\n",
    "  print(\"{}_eigvec_ratio = \\n{}\".format(dim_name, eigvec_var / eigvec))\n",
    "  \n",
    "  \n",
    "  # K principal components\n",
    "  k_principal_components_var = estimator.get_variable_value(\n",
    "    name=\"pca_vars/pca_{}_k_principal_components_var\".format(dim_name))\n",
    "  \n",
    "  print(\"{}_k_principal_components_var = \\n{}\".format(dim_name, k_principal_components_var))\n",
    "\n",
    "  return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Getting PCA data:\n",
      "\n",
      "arr_train_norm_seq.shape = (64000, 5)\n",
      "arr_train_norm_seq_features.shape = (64000, 30, 5)\n",
      "\n",
      "PCA Time Variables:\n",
      "\n",
      "X_time.shape = \n",
      "(1920000, 5)\n",
      "time_count_var = \n",
      "1920000\n",
      "time_count = \n",
      "1920000\n",
      "time_mean_var = \n",
      "[0.50607869 0.54625195 0.55298438 0.49500822 0.59269475]\n",
      "time_mean = \n",
      "[0.50607869 0.54625195 0.55298438 0.49500822 0.59269475]\n",
      "time_mean_ratio = \n",
      "[1. 1. 1. 1. 1.]\n",
      "time_cov_var = \n",
      "[[ 1.8815099  -0.24117459 -0.01856939 -0.04237986 -0.15123669]\n",
      " [-0.24117459  1.77005073  0.45672374 -0.06090154 -0.19174177]\n",
      " [-0.01856939  0.45672374  1.08040203  0.02358499  0.15794093]\n",
      " [-0.04237986 -0.06090154  0.02358499  0.63969374 -0.04129797]\n",
      " [-0.15123669 -0.19174177  0.15794093 -0.04129797  1.77602102]]\n",
      "time_cov = \n",
      "[[ 1.8815099  -0.24117459 -0.01856939 -0.04237986 -0.15123669]\n",
      " [-0.24117459  1.77005073  0.45672374 -0.06090154 -0.19174177]\n",
      " [-0.01856939  0.45672374  1.08040203  0.02358499  0.15794093]\n",
      " [-0.04237986 -0.06090154  0.02358499  0.63969374 -0.04129797]\n",
      " [-0.15123669 -0.19174177  0.15794093 -0.04129797  1.77602102]]\n",
      "time_cov_ratio = \n",
      "[[1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1.]]\n",
      "time_eigval_var = \n",
      "[0.61251761 0.80750406 1.61060967 1.94350052 2.17354558]\n",
      "time_eigval = \n",
      "[0.61251761 0.80750406 1.61060967 1.94350052 2.17354558]\n",
      "time_eigval_ratio = \n",
      "[1. 1. 1. 1. 1.]\n",
      "time_eigvec_var = \n",
      "[[ 0.07619493  0.09861413  0.62894467 -0.45203842  0.62012858]\n",
      " [ 0.18776339  0.42624831  0.34283696 -0.38501979 -0.71922171]\n",
      " [-0.26459439 -0.80170523  0.43260757 -0.04685376 -0.31291206]\n",
      " [ 0.93639135 -0.34538153 -0.06158868  0.00638794  0.00699   ]\n",
      " [ 0.11000131  0.21579611  0.5440119   0.80323405 -0.01406615]]\n",
      "time_eigvec = \n",
      "[[-0.07619493 -0.09861413  0.62894467  0.45203842 -0.62012858]\n",
      " [-0.18776339 -0.42624831  0.34283696  0.38501979  0.71922171]\n",
      " [ 0.26459439  0.80170523  0.43260757  0.04685376  0.31291206]\n",
      " [-0.93639135  0.34538153 -0.06158868 -0.00638794 -0.00699   ]\n",
      " [-0.11000131 -0.21579611  0.5440119  -0.80323405  0.01406615]]\n",
      "time_eigvec_ratio = \n",
      "[[-1. -1.  1. -1. -1.]\n",
      " [-1. -1.  1. -1. -1.]\n",
      " [-1. -1.  1. -1. -1.]\n",
      " [-1. -1.  1. -1. -1.]\n",
      " [-1. -1.  1. -1. -1.]]\n",
      "time_k_principal_components_var = \n",
      "1\n",
      "\n",
      "PCA Features Variables:\n",
      "\n",
      "X_feat.shape = \n",
      "(320000, 30)\n",
      "feat_count_var = \n",
      "320000\n",
      "feat_count = \n",
      "320000\n",
      "feat_mean_var = \n",
      "[ 0.50065724  2.06477289  0.92580521 -0.62786899 -0.19581155  0.97477253\n",
      "  1.24913092  0.57583445 -0.14414402  0.19621598  1.01620567  0.6924444\n",
      "  0.04042664  0.57859993  0.97587313  0.23918971  0.03083146  0.71055571\n",
      "  0.83846778  0.53311798  0.43437518  0.22120333  0.25847941  0.82435595\n",
      "  0.90148478  0.40313888  0.22086086  0.20684786  0.38969761  1.12258712]\n",
      "feat_mean = \n",
      "[ 0.50065724  2.06477289  0.92580521 -0.62786899 -0.19581155  0.97477253\n",
      "  1.24913092  0.57583445 -0.14414402  0.19621598  1.01620567  0.6924444\n",
      "  0.04042664  0.57859993  0.97587313  0.23918971  0.03083146  0.71055571\n",
      "  0.83846778  0.53311798  0.43437518  0.22120333  0.25847941  0.82435595\n",
      "  0.90148478  0.40313888  0.22086086  0.20684786  0.38969761  1.12258712]\n",
      "feat_mean_ratio = \n",
      "[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1.]\n",
      "feat_cov_var.shape = \n",
      "(30, 30)\n",
      "feat_cov.shape = \n",
      "(30, 30)\n",
      "feat_cov_ratio = \n",
      "[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]\n",
      " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      "  1. 1. 1. 1. 1. 1.]]\n",
      "feat_eigval_var.shape = \n",
      "(30,)\n",
      "feat_eigval.shape = \n",
      "(30,)\n",
      "feat_eigval_ratio = \n",
      "[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1.]\n",
      "feat_eigvec_var.shape = \n",
      "(30, 30)\n",
      "feat_eigvec.shape = \n",
      "(30, 30)\n",
      "feat_eigvec_ratio = \n",
      "[[ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -0.99999999 -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          0.99999999 -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]\n",
      " [ 1.          1.         -1.          1.          1.         -1.\n",
      "   1.          1.          1.          1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.          1.\n",
      "  -1.          1.          1.         -1.         -1.         -1.\n",
      "  -1.          1.         -1.         -1.         -1.         -1.        ]]\n",
      "feat_k_principal_components_var = \n",
      "1\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"model_type\"] == \"pca\":\n",
    "  print(\"Getting PCA data:\\n\")\n",
    "  arr_train_norm_seq = np.genfromtxt(\n",
    "      fname=\"data/train_norm_seq.csv\",\n",
    "      delimiter=\",\",\n",
    "      dtype=str)\n",
    "  print(\"arr_train_norm_seq.shape = {}\".format(arr_train_norm_seq.shape))\n",
    "\n",
    "  if num_tags == 1:\n",
    "    arr_train_norm_seq = np.expand_dims(a=arr_train_norm_seq, axis=-1)\n",
    "\n",
    "  arr_train_norm_seq_features = np.stack(\n",
    "      arrays=[np.stack(\n",
    "          arrays=[np.array(\n",
    "              arr_train_norm_seq[\n",
    "                  example_index,\n",
    "                  tag_index].split(\";\")).astype(np.float)\n",
    "                  for tag_index in range(num_tags)],\n",
    "          axis=1)\n",
    "              for example_index in range(len(arr_train_norm_seq))],\n",
    "      axis=0)\n",
    "\n",
    "  print(\"arr_train_norm_seq_features.shape = {}\".format(\n",
    "      arr_train_norm_seq_features.shape))\n",
    "\n",
    "  pca_data_len = arr_train_norm_seq_features.shape[0]\n",
    "  pca_data_seq_len = arr_train_norm_seq_features.shape[1]\n",
    "\n",
    "  # Time based\n",
    "  X_time = arr_train_norm_seq_features.reshape(\n",
    "      pca_data_len * pca_data_seq_len, num_tags)\n",
    "  print(\"\\nPCA Time Variables:\\n\")\n",
    "  print_pca_variables(X_time, \"time\")\n",
    "\n",
    "  # Features based\n",
    "  X_features = np.transpose(arr_train_norm_seq_features, [0, 2, 1]).reshape(\n",
    "      pca_data_len * num_tags, pca_data_seq_len)\n",
    "  print(\"\\nPCA Features Variables:\\n\")\n",
    "  print_pca_variables(X_features, \"feat\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train error distribution statistics variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Using default config.\n",
      "INFO:tensorflow:Using config: {'_device_fn': None, '_num_worker_replicas': 1, '_service': None, '_tf_random_seed': None, '_save_checkpoints_secs': 600, '_is_chief': True, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_experimental_distribute': None, '_keep_checkpoint_every_n_hours': 10000, '_global_id_in_cluster': 0, '_protocol': None, '_keep_checkpoint_max': 5, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f97c4f31a58>, '_eval_distribute': None, '_num_ps_replicas': 0, '_task_id': 0, '_train_distribute': None, '_task_type': 'worker', '_master': '', '_session_config': allow_soft_placement: true\n",
      "graph_options {\n",
      "  rewrite_options {\n",
      "    meta_optimizer_iterations: ONE\n",
      "  }\n",
      "}\n",
      ", '_log_step_count_steps': 100, '_model_dir': 'trained_model', '_evaluation_master': ''}\n",
      "INFO:tensorflow:Not using Distribute Coordinator.\n",
      "INFO:tensorflow:Running training and evaluation locally (non-distributed).\n",
      "INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n",
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'IteratorGetNext:2' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'IteratorGetNext:0' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'IteratorGetNext:1' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'IteratorGetNext:4' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'IteratorGetNext:3' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "None\n",
      "anomaly_detection: mode = \n",
      "train\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 6400, 'k_principal_components_time': None, 'throttle_secs': 120, 'num_feat_anom_thresh': 300, 'min_time_anom_thresh': 1, 'train_batch_size': 32, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 2200, 'output_dir': 'trained_model', 'do_eval': True, 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/val_norm_1_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'min_feat_anom_thresh': 1, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/val_norm_1_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'calculate_error_distribution_statistics', 'eval_batch_size': 32, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "value_b = \n",
      "Tensor(\"Abs:0\", shape=(?, 5), dtype=float64)\n",
      "mean_a = \n",
      "<tf.Variable 'mahalanobis_dist_vars/abs_err_mean_time_var:0' shape=(5,) dtype=float64_ref>\n",
      "mean_ab = \n",
      "Tensor(\"mahalanobis_dist_vars_2/cond/truediv:0\", shape=(5,), dtype=float64)\n",
      "value_b = \n",
      "Tensor(\"Abs_1:0\", shape=(?, 30), dtype=float64)\n",
      "mean_a = \n",
      "<tf.Variable 'mahalanobis_dist_vars/abs_err_mean_feat_var:0' shape=(30,) dtype=float64_ref>\n",
      "mean_ab = \n",
      "Tensor(\"mahalanobis_dist_vars_2/cond_1/truediv:0\", shape=(30,), dtype=float64)\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "INFO:tensorflow:Create CheckpointSaverHook.\n",
      "INFO:tensorflow:Graph was finalized.\n",
      "INFO:tensorflow:Restoring parameters from trained_model/model.ckpt-2000\n",
      "INFO:tensorflow:Running local_init_op.\n",
      "INFO:tensorflow:Done running local_init_op.\n",
      "INFO:tensorflow:Saving checkpoints for 2000 into trained_model/model.ckpt.\n",
      "INFO:tensorflow:loss = 0.0, step = 2001\n",
      "INFO:tensorflow:global_step/sec: 112.809\n",
      "INFO:tensorflow:loss = 0.0, step = 2101 (0.888 sec)\n",
      "INFO:tensorflow:Saving checkpoints for 2200 into trained_model/model.ckpt.\n",
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'IteratorGetNext:2' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'IteratorGetNext:0' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'IteratorGetNext:1' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'IteratorGetNext:4' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'IteratorGetNext:3' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "None\n",
      "anomaly_detection: mode = \n",
      "eval\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 6400, 'k_principal_components_time': None, 'throttle_secs': 120, 'num_feat_anom_thresh': 300, 'min_time_anom_thresh': 1, 'train_batch_size': 32, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 2200, 'output_dir': 'trained_model', 'do_eval': True, 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/val_norm_1_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'min_feat_anom_thresh': 1, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/val_norm_1_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'calculate_error_distribution_statistics', 'eval_batch_size': 32, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "INFO:tensorflow:Starting evaluation at 2019-07-16T19:44:57Z\n",
      "INFO:tensorflow:Graph was finalized.\n",
      "INFO:tensorflow:Restoring parameters from trained_model/model.ckpt-2200\n",
      "INFO:tensorflow:Running local_init_op.\n",
      "INFO:tensorflow:Done running local_init_op.\n",
      "INFO:tensorflow:Finished evaluation at 2019-07-16-19:44:59\n",
      "INFO:tensorflow:Saving dict for global step 2200: global_step = 2200, loss = 0.9952365\n",
      "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 2200: trained_model/model.ckpt-2200\n",
      "INFO:tensorflow:Loss for final step: 0.0.\n"
     ]
    }
   ],
   "source": [
    "arguments[\"training_mode\"] = \"calculate_error_distribution_statistics\"\n",
    "arguments[\"do_eval\"] = True\n",
    "arguments[\"train_file_pattern\"] = \"data/val_norm_1_seq.csv\"\n",
    "arguments[\"eval_file_pattern\"] = \"data/val_norm_1_seq.csv\"\n",
    "arguments[\"train_batch_size\"] = 32\n",
    "arguments[\"eval_batch_size\"] = 32\n",
    "if (arguments[\"model_type\"] == \"pca\" and\n",
    "   (arguments[\"k_principal_components_time\"] is None or\n",
    "    arguments[\"k_principal_components_feat\"] is None)):\n",
    "  arguments[\"previous_train_steps\"] = 2200\n",
    "else:\n",
    "  arguments[\"previous_train_steps\"] = 2000\n",
    "arguments[\"train_examples\"] = 6400\n",
    "arguments[\"eval_examples\"] = 6400\n",
    "estimator = train_and_evaluate(arguments)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Look at variable values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['OptimizeLoss/learning_rate',\n",
       " 'anom_thresh_eval_vars/fn_thresh_feat_var',\n",
       " 'anom_thresh_eval_vars/fn_thresh_time_var',\n",
       " 'anom_thresh_eval_vars/fp_thresh_feat_var',\n",
       " 'anom_thresh_eval_vars/fp_thresh_time_var',\n",
       " 'anom_thresh_eval_vars/tn_thresh_feat_var',\n",
       " 'anom_thresh_eval_vars/tn_thresh_time_var',\n",
       " 'anom_thresh_eval_vars/tp_thresh_feat_var',\n",
       " 'anom_thresh_eval_vars/tp_thresh_time_var',\n",
       " 'dummy_var',\n",
       " 'global_step',\n",
       " 'mahalanobis_dist_thresh_vars/feat_anom_thresh_var',\n",
       " 'mahalanobis_dist_thresh_vars/fn_thresh_feat_var',\n",
       " 'mahalanobis_dist_thresh_vars/fn_thresh_time_var',\n",
       " 'mahalanobis_dist_thresh_vars/fp_thresh_feat_var',\n",
       " 'mahalanobis_dist_thresh_vars/fp_thresh_time_var',\n",
       " 'mahalanobis_dist_thresh_vars/time_anom_thresh_var',\n",
       " 'mahalanobis_dist_thresh_vars/tn_thresh_feat_var',\n",
       " 'mahalanobis_dist_thresh_vars/tn_thresh_time_var',\n",
       " 'mahalanobis_dist_thresh_vars/tp_thresh_feat_var',\n",
       " 'mahalanobis_dist_thresh_vars/tp_thresh_time_var',\n",
       " 'mahalanobis_dist_vars/abs_err_count_feat_var',\n",
       " 'mahalanobis_dist_vars/abs_err_count_time_var',\n",
       " 'mahalanobis_dist_vars/abs_err_cov_feat_var',\n",
       " 'mahalanobis_dist_vars/abs_err_cov_time_var',\n",
       " 'mahalanobis_dist_vars/abs_err_inv_cov_feat_var',\n",
       " 'mahalanobis_dist_vars/abs_err_inv_cov_time_var',\n",
       " 'mahalanobis_dist_vars/abs_err_mean_feat_var',\n",
       " 'mahalanobis_dist_vars/abs_err_mean_time_var',\n",
       " 'pca_vars/pca_feat_count_var',\n",
       " 'pca_vars/pca_feat_cov_var',\n",
       " 'pca_vars/pca_feat_eigval_var',\n",
       " 'pca_vars/pca_feat_eigvec_var',\n",
       " 'pca_vars/pca_feat_k_principal_components_var',\n",
       " 'pca_vars/pca_feat_mean_var',\n",
       " 'pca_vars/pca_time_count_var',\n",
       " 'pca_vars/pca_time_cov_var',\n",
       " 'pca_vars/pca_time_eigval_var',\n",
       " 'pca_vars/pca_time_eigvec_var',\n",
       " 'pca_vars/pca_time_k_principal_components_var',\n",
       " 'pca_vars/pca_time_mean_var']"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "estimator.get_variable_names()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "arr_val_norm_1_seq.shape = (6400, 5)\n",
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'fifo_queue_DequeueUpTo:3' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'fifo_queue_DequeueUpTo:5' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'fifo_queue_DequeueUpTo:2' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'fifo_queue_DequeueUpTo:1' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'fifo_queue_DequeueUpTo:4' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "None\n",
      "anomaly_detection: mode = \n",
      "infer\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 6400, 'k_principal_components_time': None, 'throttle_secs': 120, 'num_feat_anom_thresh': 300, 'min_time_anom_thresh': 1, 'train_batch_size': 32, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 2200, 'output_dir': 'trained_model', 'do_eval': True, 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/val_norm_1_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'min_feat_anom_thresh': 1, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/val_norm_1_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'calculate_error_distribution_statistics', 'eval_batch_size': 32, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "INFO:tensorflow:Graph was finalized.\n",
      "INFO:tensorflow:Restoring parameters from trained_model/model.ckpt-2200\n",
      "INFO:tensorflow:Running local_init_op.\n",
      "INFO:tensorflow:Done running local_init_op.\n",
      "WARNING:tensorflow:From /home/jupyter/.local/lib/python3.5/site-packages/tensorflow/python/training/monitored_session.py:809: start_queue_runners (from tensorflow.python.training.queue_runner_impl) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "To construct input pipelines, use the `tf.data` module.\n"
     ]
    }
   ],
   "source": [
    "arr_val_norm_1_seq = np.genfromtxt(\n",
    "    fname=\"data/val_norm_1_seq.csv\",\n",
    "    delimiter=\",\",\n",
    "    dtype=str)\n",
    "print(\"arr_val_norm_1_seq.shape = {}\".format(arr_val_norm_1_seq.shape))\n",
    "\n",
    "if num_tags == 1:\n",
    "  arr_val_norm_1_seq = np.expand_dims(a=arr_val_norm_1_seq, axis=-1)\n",
    "\n",
    "arr_val_norm_1_seq_features = np.stack(\n",
    "    arrays=[np.stack(\n",
    "        arrays=[np.array(\n",
    "            arr_val_norm_1_seq[\n",
    "                example_index,\n",
    "                tag_index].split(\";\")).astype(np.float)\n",
    "                for tag_index in range(num_tags)],\n",
    "        axis=1)\n",
    "            for example_index in range(len(arr_val_norm_1_seq))],\n",
    "    axis=0)\n",
    "\n",
    "dict_val_norm_1_seq_features = {\n",
    "    tag: arr_val_norm_1_seq_features[:, :, index]\n",
    "    for index, tag in enumerate(arguments[\"feat_names\"])}\n",
    "\n",
    "val_norm_1_pred_list = [pred for pred in estimator.predict(\n",
    "    input_fn=tf.estimator.inputs.numpy_input_fn(\n",
    "        x=dict_val_norm_1_seq_features,\n",
    "        y=None,\n",
    "        batch_size=32,\n",
    "        num_epochs=1,\n",
    "        shuffle=False,\n",
    "        queue_capacity=1000))]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Time based"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(192000, 5)\n"
     ]
    }
   ],
   "source": [
    "val_norm_1_time_abs_err = np.stack(\n",
    "    arrays=[pred[\"X_time_abs_recon_err\"]\n",
    "            for pred in val_norm_1_pred_list],\n",
    "    axis=0)\n",
    "\n",
    "data_len = val_norm_1_time_abs_err.shape[0]\n",
    "data_seq_len = val_norm_1_time_abs_err.shape[1]\n",
    "\n",
    "time_abs_err = val_norm_1_time_abs_err.reshape(\n",
    "    data_len * data_seq_len, num_tags)\n",
    "print(time_abs_err.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Count"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "192000"
      ]
     },
     "execution_count": 100,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_count_var = estimator.get_variable_value(\n",
    "    name=\"mahalanobis_dist_vars/abs_err_count_time_var\")\n",
    "time_count_var"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "192000"
      ]
     },
     "execution_count": 101,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_count = time_abs_err.shape[0]\n",
    "time_count"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 102,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_count_var / time_count"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Mean"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.82923999, 0.65575639, 0.81758586, 0.69381041, 1.18682273])"
      ]
     },
     "execution_count": 103,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_mean_var = estimator.get_variable_value(\n",
    "    name=\"mahalanobis_dist_vars/abs_err_mean_time_var\")\n",
    "time_mean_var"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.82923999, 0.65575639, 0.81758586, 0.69381041, 1.18682273])"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_mean = np.mean(time_abs_err, axis = 0)\n",
    "time_mean"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 1., 1., 1., 1.])"
      ]
     },
     "execution_count": 105,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_mean_var / time_mean"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Covariance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.35883128  0.19521697  0.11991567  0.00985688  0.01992735]\n",
      " [ 0.19521697  0.21592857  0.07543129 -0.00294475  0.02485513]\n",
      " [ 0.11991567  0.07543129  0.19943635  0.00567749  0.0192427 ]\n",
      " [ 0.00985688 -0.00294475  0.00567749  0.15796689  0.00705319]\n",
      " [ 0.01992735  0.02485513  0.0192427   0.00705319  0.36800594]]\n"
     ]
    }
   ],
   "source": [
    "time_cov_var = estimator.get_variable_value(\n",
    "    name=\"mahalanobis_dist_vars/abs_err_cov_time_var\")\n",
    "if time_cov_var.shape[0] <= 10:\n",
    "  print(time_cov_var)\n",
    "else:\n",
    "  print(time_cov_var.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.35883128  0.19521697  0.11991567  0.00985688  0.01992735]\n",
      " [ 0.19521697  0.21592857  0.07543129 -0.00294475  0.02485513]\n",
      " [ 0.11991567  0.07543129  0.19943635  0.00567749  0.0192427 ]\n",
      " [ 0.00985688 -0.00294475  0.00567749  0.15796689  0.00705319]\n",
      " [ 0.01992735  0.02485513  0.0192427   0.00705319  0.36800594]]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"seq_len\"] == 1:\n",
    "  time_cov = np.zeros(shape=[num_tags, num_tags])\n",
    "else:\n",
    "  time_cov = np.cov(m=np.transpose(a=time_abs_err))\n",
    "\n",
    "if time_cov.shape[0] <= 10:\n",
    "  print(time_cov)\n",
    "else:\n",
    "  print(time_cov.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1.]])"
      ]
     },
     "execution_count": 108,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_cov_var / time_cov"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Inverse Covariance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 6.02122032 -4.83574477 -1.78968337 -0.40608822  0.10192415]\n",
      " [-4.83574477  9.2493467  -0.57216101  0.51003074 -0.34270535]\n",
      " [-1.78968337 -0.57216101  6.32860928 -0.11782704 -0.19310479]\n",
      " [-0.40608822  0.51003074 -0.11782704  6.3752592  -0.12848497]\n",
      " [ 0.10192415 -0.34270535 -0.19310479 -0.12848497  2.74753447]]\n"
     ]
    }
   ],
   "source": [
    "time_inv_var = estimator.get_variable_value(\n",
    "  name=\"mahalanobis_dist_vars/abs_err_inv_cov_time_var\")\n",
    "if time_inv_var.shape[0] <= 10:\n",
    "  print(time_inv_var)\n",
    "else:\n",
    "  print(time_inv_var.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 6.02122032 -4.83574477 -1.78968337 -0.40608822  0.10192415]\n",
      " [-4.83574477  9.2493467  -0.57216101  0.51003074 -0.34270535]\n",
      " [-1.78968337 -0.57216101  6.32860928 -0.11782704 -0.19310479]\n",
      " [-0.40608822  0.51003074 -0.11782704  6.3752592  -0.12848497]\n",
      " [ 0.10192415 -0.34270535 -0.19310479 -0.12848497  2.74753447]]\n"
     ]
    }
   ],
   "source": [
    "time_inv = np.linalg.inv(\n",
    "    a=time_cov + np.eye(N=num_tags) * arguments[\"eps\"])\n",
    "if time_inv.shape[0] <= 10:\n",
    "  print(time_inv)\n",
    "else:\n",
    "  print(time_inv.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1.]])"
      ]
     },
     "execution_count": 111,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_inv_var / time_inv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Features based"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(32000, 30)\n"
     ]
    }
   ],
   "source": [
    "val_norm_1_feat_abs_err = np.stack(\n",
    "    arrays=[pred[\"X_feat_abs_recon_err\"]\n",
    "            for pred in val_norm_1_pred_list],\n",
    "    axis=0)\n",
    "\n",
    "data_len = val_norm_1_feat_abs_err.shape[0]\n",
    "data_seq_len = val_norm_1_feat_abs_err.shape[1]\n",
    "\n",
    "feat_abs_err = np.transpose(\n",
    "    val_norm_1_feat_abs_err, [0, 2, 1]).reshape(\n",
    "        data_len * num_tags, data_seq_len)\n",
    "print(feat_abs_err.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Count"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "32000"
      ]
     },
     "execution_count": 113,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feat_count_var = estimator.get_variable_value(\n",
    "    name=\"mahalanobis_dist_vars/abs_err_count_feat_var\")\n",
    "feat_count_var"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "32000"
      ]
     },
     "execution_count": 114,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feat_count = feat_abs_err.shape[0]\n",
    "feat_count"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 115,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feat_count_var / feat_count"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Mean"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.25093763, 0.35322296, 0.5786495 , 0.51344137, 0.76595026,\n",
       "       0.89634027, 0.64955843, 1.07226068, 0.73159972, 0.83803563,\n",
       "       0.87935226, 0.35134181, 0.90113903, 0.45956232, 0.62551932,\n",
       "       0.7310666 , 0.42557012, 0.67707195, 0.73635177, 0.59736649,\n",
       "       0.71483493, 0.91977446, 0.42160098, 1.0803231 , 0.53929409,\n",
       "       1.04877119, 0.89002939, 0.90712537, 1.07459046, 0.78430888])"
      ]
     },
     "execution_count": 116,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feat_mean_var = estimator.get_variable_value(\n",
    "    name=\"mahalanobis_dist_vars/abs_err_mean_feat_var\")\n",
    "feat_mean_var"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.25093763, 0.35322296, 0.5786495 , 0.51344137, 0.76595026,\n",
       "       0.89634027, 0.64955843, 1.07226068, 0.73159972, 0.83803563,\n",
       "       0.87935226, 0.35134181, 0.90113903, 0.45956232, 0.62551932,\n",
       "       0.7310666 , 0.42557012, 0.67707195, 0.73635177, 0.59736649,\n",
       "       0.71483493, 0.91977446, 0.42160098, 1.0803231 , 0.53929409,\n",
       "       1.04877119, 0.89002939, 0.90712537, 1.07459046, 0.78430888])"
      ]
     },
     "execution_count": 117,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feat_mean = np.mean(feat_abs_err, axis = 0)\n",
    "feat_mean"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "       1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])"
      ]
     },
     "execution_count": 118,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feat_mean_var / feat_mean"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Covariance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(30, 30)\n"
     ]
    }
   ],
   "source": [
    "feat_cov_var = estimator.get_variable_value(\n",
    "    name=\"mahalanobis_dist_vars/abs_err_cov_feat_var\")\n",
    "if feat_cov_var.shape[0] <= 10:\n",
    "  print(feat_cov_var)\n",
    "else:\n",
    "  print(feat_cov_var.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(30, 30)\n"
     ]
    }
   ],
   "source": [
    "if num_tags == 1:\n",
    "  feat_cov = np.zeros(shape=[arguments[\"seq_len\"], arguments[\"seq_len\"]])\n",
    "else:\n",
    "  feat_cov = np.cov(m=np.transpose(a=feat_abs_err))\n",
    "\n",
    "if feat_cov.shape[0] <= 10:\n",
    "  print(feat_cov)\n",
    "else:\n",
    "  print(feat_cov.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]])"
      ]
     },
     "execution_count": 121,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feat_cov_var / feat_cov"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Inverse Covariance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(30, 30)\n"
     ]
    }
   ],
   "source": [
    "feat_inv_var = estimator.get_variable_value(\n",
    "  name=\"mahalanobis_dist_vars/abs_err_inv_cov_feat_var\")\n",
    "if feat_inv_var.shape[0] <= 10:\n",
    "  print(feat_inv_var)\n",
    "else:\n",
    "  print(feat_inv_var.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(30, 30)\n"
     ]
    }
   ],
   "source": [
    "feat_inv = np.linalg.inv(\n",
    "  a=feat_cov + np.eye(N=arguments[\"seq_len\"]) * arguments[\"eps\"])\n",
    "if feat_inv.shape[0] <= 10:\n",
    "  print(feat_inv)\n",
    "else:\n",
    "  print(feat_inv.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
       "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
       "        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]])"
      ]
     },
     "execution_count": 124,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feat_inv_var / feat_inv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tune anomaly thresholds"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Using default config.\n",
      "INFO:tensorflow:Using config: {'_device_fn': None, '_num_worker_replicas': 1, '_service': None, '_tf_random_seed': None, '_save_checkpoints_secs': 600, '_is_chief': True, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_experimental_distribute': None, '_keep_checkpoint_every_n_hours': 10000, '_global_id_in_cluster': 0, '_protocol': None, '_keep_checkpoint_max': 5, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f97c4ee68d0>, '_eval_distribute': None, '_num_ps_replicas': 0, '_task_id': 0, '_train_distribute': None, '_task_type': 'worker', '_master': '', '_session_config': allow_soft_placement: true\n",
      "graph_options {\n",
      "  rewrite_options {\n",
      "    meta_optimizer_iterations: ONE\n",
      "  }\n",
      "}\n",
      ", '_log_step_count_steps': 100, '_model_dir': 'trained_model', '_evaluation_master': ''}\n",
      "INFO:tensorflow:Not using Distribute Coordinator.\n",
      "INFO:tensorflow:Running training and evaluation locally (non-distributed).\n",
      "INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n",
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'IteratorGetNext:2' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'IteratorGetNext:0' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'IteratorGetNext:1' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'IteratorGetNext:4' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'IteratorGetNext:3' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "Tensor(\"IteratorGetNext:5\", shape=(?,), dtype=float64, device=/device:CPU:0)\n",
      "anomaly_detection: mode = \n",
      "train\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 6400, 'k_principal_components_time': None, 'throttle_secs': 120, 'num_feat_anom_thresh': 300, 'min_time_anom_thresh': 1, 'train_batch_size': 64, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 2400, 'output_dir': 'trained_model', 'do_eval': True, 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/labeled_val_mixed_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'min_feat_anom_thresh': 1, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/labeled_val_mixed_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'tune_anomaly_thresholds', 'eval_batch_size': 64, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "INFO:tensorflow:Create CheckpointSaverHook.\n",
      "INFO:tensorflow:Graph was finalized.\n",
      "INFO:tensorflow:Restoring parameters from trained_model/model.ckpt-2200\n",
      "INFO:tensorflow:Running local_init_op.\n",
      "INFO:tensorflow:Done running local_init_op.\n",
      "INFO:tensorflow:Saving checkpoints for 2200 into trained_model/model.ckpt.\n",
      "INFO:tensorflow:loss = 0.0, step = 2201\n",
      "INFO:tensorflow:global_step/sec: 28.1221\n",
      "INFO:tensorflow:loss = 0.0, step = 2301 (3.557 sec)\n",
      "INFO:tensorflow:Saving checkpoints for 2400 into trained_model/model.ckpt.\n",
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'IteratorGetNext:2' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'IteratorGetNext:0' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'IteratorGetNext:1' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'IteratorGetNext:4' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'IteratorGetNext:3' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "Tensor(\"IteratorGetNext:5\", shape=(?,), dtype=float64, device=/device:CPU:0)\n",
      "anomaly_detection: mode = \n",
      "eval\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 6400, 'k_principal_components_time': None, 'throttle_secs': 120, 'num_feat_anom_thresh': 300, 'min_time_anom_thresh': 1, 'train_batch_size': 64, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 2400, 'output_dir': 'trained_model', 'do_eval': True, 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/labeled_val_mixed_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'min_feat_anom_thresh': 1, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/labeled_val_mixed_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'tune_anomaly_thresholds', 'eval_batch_size': 64, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "INFO:tensorflow:Starting evaluation at 2019-07-16T19:45:11Z\n",
      "INFO:tensorflow:Graph was finalized.\n",
      "INFO:tensorflow:Restoring parameters from trained_model/model.ckpt-2400\n",
      "INFO:tensorflow:Running local_init_op.\n",
      "INFO:tensorflow:Done running local_init_op.\n",
      "INFO:tensorflow:Finished evaluation at 2019-07-16-19:45:14\n",
      "INFO:tensorflow:Saving dict for global step 2400: feat_anom_acc = 1.0, feat_anom_f_beta = 1.0, feat_anom_fn = 0, feat_anom_fp = 0, feat_anom_pre = 1.0, feat_anom_rec = 1.0, feat_anom_tn = 6400, feat_anom_tp = 6400, global_step = 2400, loss = 0.0, time_anom_acc = 1.0, time_anom_f_beta = 1.0, time_anom_fn = 0, time_anom_fp = 0, time_anom_pre = 1.0, time_anom_rec = 1.0, time_anom_tn = 6400, time_anom_tp = 6400\n",
      "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 2400: trained_model/model.ckpt-2400\n",
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'StringToNumber:0' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'StringToNumber_1:0' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'StringToNumber_2:0' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'StringToNumber_3:0' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'StringToNumber_4:0' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "None\n",
      "anomaly_detection: mode = \n",
      "infer\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 6400, 'k_principal_components_time': None, 'throttle_secs': 120, 'num_feat_anom_thresh': 300, 'min_time_anom_thresh': 1, 'train_batch_size': 64, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 2400, 'output_dir': 'trained_model', 'do_eval': True, 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/labeled_val_mixed_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'min_feat_anom_thresh': 1, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/labeled_val_mixed_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'tune_anomaly_thresholds', 'eval_batch_size': 64, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "WARNING:tensorflow:From /home/jupyter/.local/lib/python3.5/site-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:205: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\n",
      "INFO:tensorflow:Signatures INCLUDED in export for Classify: None\n",
      "INFO:tensorflow:Signatures INCLUDED in export for Eval: None\n",
      "INFO:tensorflow:Signatures INCLUDED in export for Predict: ['serving_default', 'predict_export_outputs']\n",
      "INFO:tensorflow:Signatures INCLUDED in export for Regress: None\n",
      "INFO:tensorflow:Signatures INCLUDED in export for Train: None\n",
      "INFO:tensorflow:Restoring parameters from trained_model/model.ckpt-2400\n",
      "INFO:tensorflow:Assets added to graph.\n",
      "INFO:tensorflow:No assets to write.\n",
      "INFO:tensorflow:SavedModel written to: trained_model/export/exporter/temp-b'1563306314'/saved_model.pb\n",
      "INFO:tensorflow:Loss for final step: 0.0.\n"
     ]
    }
   ],
   "source": [
    "arguments[\"training_mode\"] = \"tune_anomaly_thresholds\"\n",
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  arguments[\"do_eval\"] = True\n",
    "  arguments[\"train_file_pattern\"] = \"data/labeled_val_mixed_seq.csv\"\n",
    "  arguments[\"eval_file_pattern\"] = \"data/labeled_val_mixed_seq.csv\"\n",
    "else:\n",
    "  arguments[\"train_file_pattern\"] = \"data/unlabeled_val_mixed_seq.csv\"\n",
    "  arguments[\"train_file_pattern\"] = \"data/unlabeled_val_mixed_seq.csv\"\n",
    "arguments[\"train_batch_size\"] = 64\n",
    "arguments[\"eval_batch_size\"] = 64\n",
    "if arguments[\"model_type\"] == \"pca\":\n",
    "  arguments[\"previous_train_steps\"] = 2400\n",
    "else:\n",
    "  arguments[\"previous_train_steps\"] = 2200\n",
    "arguments[\"train_examples\"] = 6400\n",
    "arguments[\"eval_examples\"] = 6400\n",
    "estimator = train_and_evaluate(arguments)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Labeled"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Time based"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6399 6399 6398 6398 6397 6397\n",
      " 6397 6391 6391 6391 6390 6388 6387 6386 6383 6380 6380 6379 6376 6372\n",
      " 6369 6367 6366 6363 6359 6355 6347 6340 6330 6322 6316 6309 6296 6279\n",
      " 6263 6249 6238 6218 6211 6195 6181 6168 6148 6129 6112 6086 6060 6042\n",
      " 6020 6003 5978 5944 5908 5874 5832 5785 5748 5718 5681 5636 5594 5537\n",
      " 5492 5450 5397 5354 5303 5245 5180 5141 5087 5026 4976 4915 4859 4799\n",
      " 4735 4670 4601 4542 4462 4386 4316 4261 4195 4119 4045 3980 3918 3839\n",
      " 3758 3687 3611 3537 3469 3389 3313 3232 3158 3088 3009 2948 2878 2807\n",
      " 2727 2662 2597 2539 2483 2428 2358 2284 2207 2136 2074 1993 1941 1873\n",
      " 1818 1757 1696 1650 1603 1552]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/tp_thresh_time_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 127,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[   0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    1    1    2    2    3    3\n",
      "    3    9    9    9   10   12   13   14   17   20   20   21   24   28\n",
      "   31   33   34   37   41   45   53   60   70   78   84   91  104  121\n",
      "  137  151  162  182  189  205  219  232  252  271  288  314  340  358\n",
      "  380  397  422  456  492  526  568  615  652  682  719  764  806  863\n",
      "  908  950 1003 1046 1097 1155 1220 1259 1313 1374 1424 1485 1541 1601\n",
      " 1665 1730 1799 1858 1938 2014 2084 2139 2205 2281 2355 2420 2482 2561\n",
      " 2642 2713 2789 2863 2931 3011 3087 3168 3242 3312 3391 3452 3522 3593\n",
      " 3673 3738 3803 3861 3917 3972 4042 4116 4193 4264 4326 4407 4459 4527\n",
      " 4582 4643 4704 4750 4797 4848]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/fn_thresh_time_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[6400 6400 6400 6400 6400 6399 5655 2624  416   19    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/fp_thresh_time_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 129,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[   0    0    0    0    0    1  745 3776 5984 6381 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/tn_thresh_time_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 130,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.311036789297659\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/time_anom_thresh_var\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Features based"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 131,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/tp_thresh_feat_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 132,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/fn_thresh_feat_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 133,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6364\n",
      " 5805 3847 1469  335   52    7    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/fp_thresh_feat_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 134,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[   0    0    0    0    0    0    0    0    0    0    0    0    0   36\n",
      "  595 2553 4931 6065 6348 6393 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/tn_thresh_feat_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 135,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7.622073578595318\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/feat_anom_thresh_var\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Numpy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 137,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "arr_val_mixed_seq.shape = (12800, 6)\n",
      "arr_val_mixed_seq_feat.shape = (12800, 30, 5)\n",
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'fifo_queue_DequeueUpTo:3' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'fifo_queue_DequeueUpTo:5' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'fifo_queue_DequeueUpTo:2' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'fifo_queue_DequeueUpTo:1' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'fifo_queue_DequeueUpTo:4' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "None\n",
      "anomaly_detection: mode = \n",
      "infer\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 6400, 'k_principal_components_time': None, 'throttle_secs': 120, 'num_feat_anom_thresh': 300, 'min_time_anom_thresh': 1, 'train_batch_size': 64, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 2400, 'output_dir': 'trained_model', 'do_eval': True, 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/labeled_val_mixed_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'min_feat_anom_thresh': 1, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/labeled_val_mixed_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'tune_anomaly_thresholds', 'eval_batch_size': 64, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "INFO:tensorflow:Graph was finalized.\n",
      "INFO:tensorflow:Restoring parameters from trained_model/model.ckpt-2400\n",
      "INFO:tensorflow:Running local_init_op.\n",
      "INFO:tensorflow:Done running local_init_op.\n",
      "arr_val_mixed_seq_labels.shape = (12800,)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  arr_val_mixed_seq = np.genfromtxt(\n",
    "      fname=\"data/labeled_val_mixed_seq.csv\", delimiter=\",\", dtype=str)\n",
    "  print(\"arr_val_mixed_seq.shape = {}\".format(arr_val_mixed_seq.shape))\n",
    "\n",
    "  arr_val_mixed_seq_feat = np.stack(\n",
    "      arrays=[np.stack(\n",
    "          arrays=[np.array(\n",
    "              object=arr_val_mixed_seq[\n",
    "                  example_index,\n",
    "                  tag_index].split(\";\")).astype(np.float)\n",
    "                  for tag_index in range(num_tags)],\n",
    "          axis=1)\n",
    "              for example_index in range(len(arr_val_mixed_seq))],\n",
    "      axis=0)\n",
    "  print(\"arr_val_mixed_seq_feat.shape = {}\".format(arr_val_mixed_seq_feat.shape))\n",
    "\n",
    "  dict_val_mixed_seq_feat = {\n",
    "      tag: arr_val_mixed_seq_feat[:, :, index]\n",
    "      for index, tag in enumerate(arguments[\"feat_names\"])}\n",
    "\n",
    "  val_mixed_pred_list = [pred for pred in estimator.predict(\n",
    "      input_fn=tf.estimator.inputs.numpy_input_fn(\n",
    "          x=dict_val_mixed_seq_feat,\n",
    "          y=None,\n",
    "          batch_size=128,\n",
    "          num_epochs=1,\n",
    "          shuffle=False,\n",
    "          queue_capacity=1000))]\n",
    "\n",
    "  arr_val_mixed_seq_labels = arr_val_mixed_seq[:, -1].astype(np.float64)\n",
    "  print(\"arr_val_mixed_seq_labels.shape = {}\".format(\n",
    "      arr_val_mixed_seq_labels.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 138,
   "metadata": {},
   "outputs": [],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  labels_norm_mask = arr_val_mixed_seq_labels.astype(np.float64) == 0\n",
    "  labels_anom_mask = arr_val_mixed_seq_labels.astype(np.float64) == 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 139,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1. 0. 0. 0. 0. 1. 1. 1. 0. 1.]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(arr_val_mixed_seq_labels[0:10])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Time based"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 140,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "arr_val_mixed_pred_mahalanobis_dist_time.shape = (12800, 30)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  arr_val_mixed_pred_mahalanobis_dist_time = np.stack(\n",
    "      arrays=[pred[\"mahalanobis_dist_time\"]\n",
    "              for pred in val_mixed_pred_list], axis=0)\n",
    "  print(\"arr_val_mixed_pred_mahalanobis_dist_time.shape = {}\".format(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_time.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 141,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "min_norm = 2.6500027832863093 & max_norm = 4.0886070205171885\n",
      "min_anom = 59.2222285749923 & max_anom = 137.1693406139394\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  min_norm_mahalanobis_dist_time = np.min(np.max(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_time[labels_norm_mask, :],\n",
    "      axis=-1))\n",
    "  max_norm_mahalanobis_dist_time = np.max(np.max(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_time[labels_norm_mask, :],\n",
    "      axis=-1))\n",
    "  print(\"min_norm = {} & max_norm = {}\".format(\n",
    "      min_norm_mahalanobis_dist_time,\n",
    "      max_norm_mahalanobis_dist_time))\n",
    "\n",
    "  min_anom_mahalanobis_dist_time = np.min(np.max(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_time[labels_anom_mask, :],\n",
    "      axis=-1))\n",
    "  max_anom_mahalanobis_dist_time = np.max(np.max(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_time[labels_anom_mask, :],\n",
    "      axis=-1))\n",
    "  print(\"min_anom = {} & max_anom = {}\".format(\n",
    "      min_anom_mahalanobis_dist_time,\n",
    "      max_anom_mahalanobis_dist_time))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 142,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time_anom_thresh.shape = (300,)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  num_time_anom_thresh = arguments[\"num_time_anom_thresh\"]\n",
    "  time_anom_thresh = np.linspace(\n",
    "      start = arguments[\"min_time_anom_thresh\"],\n",
    "      stop = arguments[\"max_time_anom_thresh\"],\n",
    "      num = num_time_anom_thresh)\n",
    "  print(\"time_anom_thresh.shape = {}\".format(\n",
    "      time_anom_thresh.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 143,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh.shape = (12800, 30, 300)\n",
      "arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh.shape = (12800, 300)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh = np.stack(\n",
    "      arrays = [arr_val_mixed_pred_mahalanobis_dist_time > anom_thresh\n",
    "                for anom_thresh in time_anom_thresh],\n",
    "      axis = -1)\n",
    "  print(\"arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh.shape = {}\".format(\n",
    "      arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh.shape))\n",
    "  arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh = np.any(\n",
    "      a = arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh,\n",
    "      axis = 1)\n",
    "  print(\"arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh.shape = {}\".format(\n",
    "      arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 144,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "predicted_norms.shape = (12800, 300)\n",
      "predicted_anoms.shape = (12800, 300)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  predicted_norms = arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh == 0\n",
    "  print(\"predicted_norms.shape = {}\".format(predicted_norms.shape))\n",
    "  predicted_anoms = arr_val_mix_pred_mahalanobis_dist_time_anom_multi_thresh == 1\n",
    "  print(\"predicted_anoms.shape = {}\".format(predicted_anoms.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 145,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tp.shape = (300,), fn.shape = (300,), fp.shape = (300,), tn.shape = (300,)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  true_positives = np.sum(\n",
    "      a = np.stack(arrays = [np.logical_and(\n",
    "          labels_anom_mask, predicted_anoms[:, threshold])\n",
    "                             for threshold in range(num_time_anom_thresh)],\n",
    "                   axis = -1),\n",
    "      axis = 0)\n",
    "\n",
    "  false_negatives = np.sum(\n",
    "      a = np.stack(arrays = [np.logical_and(\n",
    "          labels_anom_mask, predicted_norms[:, threshold])\n",
    "                             for threshold in range(num_time_anom_thresh)],\n",
    "                   axis = -1),\n",
    "      axis = 0)\n",
    "\n",
    "  false_positives = np.sum(\n",
    "      a = np.stack(arrays = [np.logical_and(\n",
    "          labels_norm_mask, predicted_anoms[:, threshold])\n",
    "                             for threshold in range(num_time_anom_thresh)],\n",
    "                   axis = -1),\n",
    "      axis = 0)\n",
    "\n",
    "  true_negatives = np.sum(\n",
    "      a = np.stack(arrays = [np.logical_and(\n",
    "          labels_norm_mask, predicted_norms[:, threshold])\n",
    "                             for threshold in range(num_time_anom_thresh)],\n",
    "                   axis = -1),\n",
    "      axis = 0)\n",
    "  print(\"tp.shape = {}, fn.shape = {}, fp.shape = {}, tn.shape = {}\".format(\n",
    "      true_positives.shape,\n",
    "      false_negatives.shape,\n",
    "      false_positives.shape,\n",
    "      true_negatives.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 146,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true_positives = \n",
      "[6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6399 6399 6398 6398 6397 6397\n",
      " 6397 6391 6391 6391 6390 6388 6387 6386 6383 6380 6380 6379 6376 6372\n",
      " 6369 6367 6366 6363 6359 6355 6347 6340 6330 6322 6316 6309 6296 6279\n",
      " 6263 6249 6238 6218 6211 6195 6181 6168 6148 6129 6112 6086 6060 6042\n",
      " 6020 6003 5978 5944 5908 5874 5832 5785 5748 5718 5681 5636 5594 5537\n",
      " 5492 5450 5397 5354 5303 5245 5180 5141 5087 5026 4976 4915 4859 4799\n",
      " 4735 4670 4601 4542 4462 4386 4316 4261 4195 4119 4045 3980 3918 3839\n",
      " 3758 3687 3611 3537 3469 3389 3313 3232 3158 3088 3009 2948 2878 2807\n",
      " 2727 2662 2597 2539 2483 2428 2358 2284 2207 2136 2074 1993 1941 1873\n",
      " 1818 1757 1696 1650 1603 1552]\n",
      "false_negatives = \n",
      "[   0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    1    1    2    2    3    3\n",
      "    3    9    9    9   10   12   13   14   17   20   20   21   24   28\n",
      "   31   33   34   37   41   45   53   60   70   78   84   91  104  121\n",
      "  137  151  162  182  189  205  219  232  252  271  288  314  340  358\n",
      "  380  397  422  456  492  526  568  615  652  682  719  764  806  863\n",
      "  908  950 1003 1046 1097 1155 1220 1259 1313 1374 1424 1485 1541 1601\n",
      " 1665 1730 1799 1858 1938 2014 2084 2139 2205 2281 2355 2420 2482 2561\n",
      " 2642 2713 2789 2863 2931 3011 3087 3168 3242 3312 3391 3452 3522 3593\n",
      " 3673 3738 3803 3861 3917 3972 4042 4116 4193 4264 4326 4407 4459 4527\n",
      " 4582 4643 4704 4750 4797 4848]\n",
      "false_positives = \n",
      "[6400 6400 6400 6400 6400 6399 5655 2624  416   19    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0]\n",
      "true_negatives = \n",
      "[   0    0    0    0    0    1  745 3776 5984 6381 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400]\n",
      "tp + fn + fp + tn = \n",
      "[12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(\"true_positives = \\n{}\".format(true_positives))\n",
    "  print(\"false_negatives = \\n{}\".format(false_negatives))\n",
    "  print(\"false_positives = \\n{}\".format(false_positives))\n",
    "  print(\"true_negatives = \\n{}\".format(true_negatives))\n",
    "  print(\"tp + fn + fp + tn = \\n{}\".format(\n",
    "      true_positives + false_negatives + false_positives + true_negatives))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 147,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.5        0.5        0.5        0.5        0.5        0.50007813\n",
      " 0.55820313 0.795      0.9675     0.99851563 1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         0.99992187 0.99992187 0.99984375 0.99984375\n",
      " 0.99976562 0.99976562 0.99976562 0.99929687 0.99929687 0.99929687\n",
      " 0.99921875 0.9990625  0.99898438 0.99890625 0.99867187 0.9984375\n",
      " 0.9984375  0.99835938 0.998125   0.9978125  0.99757812 0.99742188\n",
      " 0.99734375 0.99710937 0.99679688 0.99648437 0.99585937 0.9953125\n",
      " 0.99453125 0.99390625 0.9934375  0.99289063 0.991875   0.99054688\n",
      " 0.98929687 0.98820312 0.98734375 0.98578125 0.98523437 0.98398438\n",
      " 0.98289063 0.981875   0.9803125  0.97882813 0.9775     0.97546875\n",
      " 0.9734375  0.97203125 0.9703125  0.96898438 0.96703125 0.964375\n",
      " 0.9615625  0.95890625 0.955625   0.95195312 0.9490625  0.94671875\n",
      " 0.94382813 0.9403125  0.93703125 0.93257813 0.9290625  0.92578125\n",
      " 0.92164062 0.91828125 0.91429688 0.90976563 0.9046875  0.90164062\n",
      " 0.89742188 0.89265625 0.88875    0.88398438 0.87960938 0.87492187\n",
      " 0.86992187 0.86484375 0.85945313 0.85484375 0.84859375 0.84265625\n",
      " 0.8371875  0.83289062 0.82773438 0.82179687 0.81601563 0.8109375\n",
      " 0.80609375 0.79992188 0.79359375 0.78804688 0.78210937 0.77632813\n",
      " 0.77101562 0.76476563 0.75882812 0.7525     0.74671875 0.74125\n",
      " 0.73507813 0.7303125  0.72484375 0.71929687 0.71304687 0.70796875\n",
      " 0.70289062 0.69835938 0.69398437 0.6896875  0.68421875 0.6784375\n",
      " 0.67242187 0.666875   0.66203125 0.65570313 0.65164062 0.64632813\n",
      " 0.64203125 0.63726562 0.6325     0.62890625 0.62523438 0.62125   ]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  acc_num = true_positives + true_negatives\n",
    "  acc_den = true_positives + false_negatives + false_positives + true_negatives\n",
    "  accuracy = acc_num / acc_den\n",
    "  print(accuracy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 148,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.5        0.5        0.5        0.5        0.5        0.50003907\n",
      " 0.53090004 0.70921986 0.93896714 0.99704004 1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.        ]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  precision = true_positives / (true_positives + false_positives)\n",
    "  print(precision)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 149,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         0.99984375 0.99984375 0.9996875  0.9996875\n",
      " 0.99953125 0.99953125 0.99953125 0.99859375 0.99859375 0.99859375\n",
      " 0.9984375  0.998125   0.99796875 0.9978125  0.99734375 0.996875\n",
      " 0.996875   0.99671875 0.99625    0.995625   0.99515625 0.99484375\n",
      " 0.9946875  0.99421875 0.99359375 0.99296875 0.99171875 0.990625\n",
      " 0.9890625  0.9878125  0.986875   0.98578125 0.98375    0.98109375\n",
      " 0.97859375 0.97640625 0.9746875  0.9715625  0.97046875 0.96796875\n",
      " 0.96578125 0.96375    0.960625   0.95765625 0.955      0.9509375\n",
      " 0.946875   0.9440625  0.940625   0.93796875 0.9340625  0.92875\n",
      " 0.923125   0.9178125  0.91125    0.90390625 0.898125   0.8934375\n",
      " 0.88765625 0.880625   0.8740625  0.86515625 0.858125   0.8515625\n",
      " 0.84328125 0.8365625  0.82859375 0.81953125 0.809375   0.80328125\n",
      " 0.79484375 0.7853125  0.7775     0.76796875 0.75921875 0.74984375\n",
      " 0.73984375 0.7296875  0.71890625 0.7096875  0.6971875  0.6853125\n",
      " 0.674375   0.66578125 0.65546875 0.64359375 0.63203125 0.621875\n",
      " 0.6121875  0.59984375 0.5871875  0.57609375 0.56421875 0.55265625\n",
      " 0.54203125 0.52953125 0.51765625 0.505      0.4934375  0.4825\n",
      " 0.47015625 0.460625   0.4496875  0.43859375 0.42609375 0.4159375\n",
      " 0.40578125 0.39671875 0.38796875 0.379375   0.3684375  0.356875\n",
      " 0.34484375 0.33375    0.3240625  0.31140625 0.30328125 0.29265625\n",
      " 0.2840625  0.27453125 0.265      0.2578125  0.25046875 0.2425    ]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  recall = true_positives / (true_positives + false_negatives)\n",
    "  print(recall)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 150,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.50062422 0.50062422 0.50062422 0.50062422 0.50062422 0.50066329\n",
      " 0.53152183 0.70973451 0.93911007 0.9970474  1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         0.99999961 0.99999961 0.99999922 0.99999922\n",
      " 0.99999883 0.99999883 0.99999883 0.99999649 0.99999649 0.99999649\n",
      " 0.9999961  0.99999532 0.99999492 0.99999453 0.99999336 0.99999218\n",
      " 0.99999218 0.99999179 0.99999061 0.99998904 0.99998786 0.99998708\n",
      " 0.99998668 0.9999855  0.99998392 0.99998234 0.99997918 0.9999764\n",
      " 0.99997242 0.99996923 0.99996684 0.99996403 0.99995881 0.99995195\n",
      " 0.99994545 0.99993974 0.99993524 0.99992701 0.99992412 0.99991749\n",
      " 0.99991165 0.99990621 0.99989779 0.99988975 0.99988251 0.99987135\n",
      " 0.99986011 0.99985226 0.99984261 0.99983511 0.99982399 0.99980872\n",
      " 0.99979237 0.99977674 0.99975718 0.99973496 0.99971721 0.99970265\n",
      " 0.99968448 0.99966207 0.99964082 0.99961147 0.99958787 0.9995655\n",
      " 0.99953676 0.99951304 0.9994844  0.99945115 0.99941301 0.99938966\n",
      " 0.99935675 0.99931872 0.99928686 0.99924711 0.99920974 0.99916874\n",
      " 0.99912387 0.99907704 0.99902588 0.99898091 0.99891804 0.9988562\n",
      " 0.99879732 0.99874971 0.99869093 0.99862092 0.99855023 0.99848599\n",
      " 0.99842272 0.99833917 0.99824987 0.99816838 0.99807761 0.99798551\n",
      " 0.99789742 0.99778928 0.99768174 0.99756158 0.99744644 0.99733247\n",
      " 0.99719752 0.99708839 0.9969575  0.9968181  0.99665239 0.99651046\n",
      " 0.99636146 0.99622211 0.99608143 0.99593699 0.99574347 0.99552609\n",
      " 0.99528453 0.99504647 0.99482536 0.99451593 0.99430378 0.99400873\n",
      " 0.99375409 0.99345319 0.99313084 0.99287215 0.99259265 0.99227041]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  f_beta_score_num = (1. + arguments[\"f_score_beta\"] ** 2) * (precision * recall)\n",
    "  f_beta_score_den = arguments[\"f_score_beta\"] ** 2 * precision + recall\n",
    "  f_beta_score = f_beta_score_num / f_beta_score_den\n",
    "  print(f_beta_score)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 151,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.311036789297659\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(time_anom_thresh[np.argmax(f_beta_score)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Features based"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 152,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "arr_val_mixed_pred_mahalanobis_dist_feat.shape = (12800, 5)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  arr_val_mixed_pred_mahalanobis_dist_feat = np.stack(\n",
    "      arrays=[pred[\"mahalanobis_dist_feat\"]\n",
    "              for pred in val_mixed_pred_list], axis=0)\n",
    "  print(\"arr_val_mixed_pred_mahalanobis_dist_feat.shape = {}\".format(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_feat.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 153,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "min_norm = 5.070002128516675 & max_norm = 7.547333116473872\n",
      "min_anom = 144.9739060124391 & max_anom = 316.7225535693391\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  min_norm_mahalanobis_dist_feat = np.min(np.max(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_feat[labels_norm_mask, :],\n",
    "      axis=-1))\n",
    "  max_norm_mahalanobis_dist_feat = np.max(np.max(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_feat[labels_norm_mask, :],\n",
    "      axis=-1))\n",
    "  print(\"min_norm = {} & max_norm = {}\".format(\n",
    "      min_norm_mahalanobis_dist_feat,\n",
    "      max_norm_mahalanobis_dist_feat))\n",
    "\n",
    "  min_anom_mahalanobis_dist_feat = np.min(np.max(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_feat[labels_anom_mask, :],\n",
    "      axis=-1))\n",
    "  max_anom_mahalanobis_dist_feat = np.max(np.max(\n",
    "      arr_val_mixed_pred_mahalanobis_dist_feat[labels_anom_mask, :],\n",
    "      axis=-1))\n",
    "  print(\"min_anom = {} & max_anom = {}\".format(\n",
    "      min_anom_mahalanobis_dist_feat,\n",
    "      max_anom_mahalanobis_dist_feat))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "feat_anom_thresh.shape = (300,)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  num_feat_anom_thresh = arguments[\"num_feat_anom_thresh\"]\n",
    "  feat_anom_thresh = np.linspace(\n",
    "      start = arguments[\"min_feat_anom_thresh\"],\n",
    "      stop = arguments[\"max_feat_anom_thresh\"],\n",
    "      num = num_feat_anom_thresh)\n",
    "  print(\"feat_anom_thresh.shape = {}\".format(\n",
    "      feat_anom_thresh.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 155,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh.shape = (12800, 5, 300)\n",
      "arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh.shape = (12800, 300)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh = np.stack(\n",
    "      arrays = [arr_val_mixed_pred_mahalanobis_dist_feat > anom_thresh\n",
    "                for anom_thresh in feat_anom_thresh],\n",
    "      axis = -1)\n",
    "  print(\"arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh.shape = {}\".format(\n",
    "      arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh.shape))\n",
    "  arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh = np.any(\n",
    "      a = arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh,\n",
    "      axis = 1)\n",
    "  print(\"arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh.shape = {}\".format(\n",
    "      arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 156,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "predicted_norms.shape = (12800, 300)\n",
      "predicted_anoms.shape = (12800, 300)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  predicted_norms = arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh == 0\n",
    "  print(\"predicted_norms.shape = {}\".format(predicted_norms.shape))\n",
    "  predicted_anoms = arr_val_mix_pred_mahalanobis_dist_feat_anom_multi_thresh == 1\n",
    "  print(\"predicted_anoms.shape = {}\".format(predicted_anoms.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 157,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tp.shape = (300,), fn.shape = (300,), fp.shape = (300,), tn.shape = (300,)\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  true_positives = np.sum(\n",
    "      a = np.stack(arrays = [np.logical_and(\n",
    "          labels_anom_mask, predicted_anoms[:, threshold])\n",
    "                             for threshold in range(num_feat_anom_thresh)],\n",
    "                   axis = -1),\n",
    "      axis = 0)\n",
    "\n",
    "  false_negatives = np.sum(\n",
    "      a = np.stack(arrays = [np.logical_and(\n",
    "          labels_anom_mask, predicted_norms[:, threshold])\n",
    "                             for threshold in range(num_feat_anom_thresh)],\n",
    "                   axis = -1),\n",
    "      axis = 0)\n",
    "\n",
    "  false_positives = np.sum(\n",
    "      a = np.stack(arrays = [np.logical_and(\n",
    "          labels_norm_mask, predicted_anoms[:, threshold])\n",
    "                             for threshold in range(num_feat_anom_thresh)],\n",
    "                   axis = -1),\n",
    "      axis = 0)\n",
    "\n",
    "  true_negatives = np.sum(\n",
    "      a = np.stack(arrays = [np.logical_and(\n",
    "          labels_norm_mask, predicted_norms[:, threshold])\n",
    "                             for threshold in range(num_feat_anom_thresh)],\n",
    "                   axis = -1),\n",
    "      axis = 0)\n",
    "  print(\"tp.shape = {}, fn.shape = {}, fp.shape = {}, tn.shape = {}\".format(\n",
    "      true_positives.shape,\n",
    "      false_negatives.shape,\n",
    "      false_positives.shape,\n",
    "      true_negatives.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 158,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true_positives = \n",
      "[6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400]\n",
      "false_negatives = \n",
      "[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
      " 0 0 0 0]\n",
      "false_positives = \n",
      "[6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6364\n",
      " 5805 3847 1469  335   52    7    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0    0    0    0    0    0    0    0    0\n",
      "    0    0    0    0    0    0]\n",
      "true_negatives = \n",
      "[   0    0    0    0    0    0    0    0    0    0    0    0    0   36\n",
      "  595 2553 4931 6065 6348 6393 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400\n",
      " 6400 6400 6400 6400 6400 6400]\n",
      "true_positives + false_negatives + false_positives + true_negatives = \n",
      "[12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800\n",
      " 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800 12800]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(\"true_positives = \\n{}\".format(true_positives))\n",
    "  print(\"false_negatives = \\n{}\".format(false_negatives))\n",
    "  print(\"false_positives = \\n{}\".format(false_positives))\n",
    "  print(\"true_negatives = \\n{}\".format(true_negatives))\n",
    "  print(\"true_positives + false_negatives + false_positives + true_negatives = \\n{}\".format(true_positives + false_negatives + false_positives + true_negatives))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 159,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.5        0.5        0.5        0.5        0.5        0.5\n",
      " 0.5        0.5        0.5        0.5        0.5        0.5\n",
      " 0.5        0.5028125  0.54648438 0.69945313 0.88523438 0.97382813\n",
      " 0.9959375  0.99945313 1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.        ]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  acc_num = true_positives + true_negatives\n",
    "  acc_den = true_positives + false_negatives + false_positives + true_negatives\n",
    "  accuracy = acc_num / acc_den\n",
    "  print(accuracy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.5        0.5        0.5        0.5        0.5        0.5\n",
      " 0.5        0.5        0.5        0.5        0.5        0.5\n",
      " 0.5        0.50141022 0.52437526 0.62457305 0.81331808 0.95025984\n",
      " 0.99194048 0.99890744 1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.        ]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  precision = true_positives / (true_positives + false_positives)\n",
    "  print(precision)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 161,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
      " 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  recall = true_positives / (true_positives + false_negatives)\n",
    "  print(recall)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 162,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.50062422 0.50062422 0.50062422 0.50062422 0.50062422 0.50062422\n",
      " 0.50062422 0.50062422 0.50062422 0.50062422 0.50062422 0.50062422\n",
      " 0.50062422 0.50203443 0.52499795 0.62515834 0.81369689 0.95037772\n",
      " 0.99196042 0.99891017 1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.\n",
      " 1.         1.         1.         1.         1.         1.        ]\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  f_beta_score_num = (1. + arguments[\"f_score_beta\"] ** 2) * (precision * recall)\n",
    "  f_beta_score_den = arguments[\"f_score_beta\"] ** 2 * precision + recall\n",
    "  f_beta_score = f_beta_score_num / f_beta_score_den\n",
    "  print(f_beta_score)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 163,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7.622073578595318\n"
     ]
    }
   ],
   "source": [
    "if arguments[\"labeled_tune_thresh\"]:\n",
    "  print(feat_anom_thresh[np.argmax(f_beta_score)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Unlabeled"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Time based"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/count_thresh_feat_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 165,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/mean_thresh_time_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/var_thresh_time_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 167,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/time_anom_thresh_var\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Feature based"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 168,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/count_thresh_feat_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 169,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/mean_thresh_feat_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 170,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/var_thresh_feat_var\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 171,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not arguments[\"labeled_tune_thresh\"]:\n",
    "  print(estimator.get_variable_value(\n",
    "      name=\"mahalanobis_dist_thresh_vars/feat_anom_thresh_var\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Local Prediction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 173,
   "metadata": {},
   "outputs": [],
   "source": [
    "arr_labeled_test_mixed_seq = np.genfromtxt(\n",
    "    fname=\"data/labeled_test_mixed_seq.csv\",\n",
    "    delimiter=\",\",\n",
    "    dtype=str)\n",
    "arr_labeled_test_mixed_seq_features = np.stack(\n",
    "    arrays=[np.stack(\n",
    "        arrays=[np.array(arr_labeled_test_mixed_seq[\n",
    "                           example_index,\n",
    "                           tag_index].split(';')).astype(np.float)\n",
    "                  for tag_index in range(num_tags)], axis=1)\n",
    "              for example_index in range(len(arr_labeled_test_mixed_seq))],\n",
    "    axis=0)\n",
    "dict_labeled_test_mixed_sequences_features = {\n",
    "    tag: arr_labeled_test_mixed_seq_features[:, :, index]\n",
    "    for index, tag in enumerate(arguments[\"feat_names\"])}\n",
    "arr_test_labels = arr_labeled_test_mixed_seq[:, -1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 174,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Calling model_fn.\n",
      "\n",
      "anomaly_detection: features = \n",
      "{'tag_2': <tf.Tensor 'fifo_queue_DequeueUpTo:3' shape=(?, 30) dtype=float64>, 'tag_4': <tf.Tensor 'fifo_queue_DequeueUpTo:5' shape=(?, 30) dtype=float64>, 'tag_1': <tf.Tensor 'fifo_queue_DequeueUpTo:2' shape=(?, 30) dtype=float64>, 'tag_0': <tf.Tensor 'fifo_queue_DequeueUpTo:1' shape=(?, 30) dtype=float64>, 'tag_3': <tf.Tensor 'fifo_queue_DequeueUpTo:4' shape=(?, 30) dtype=float64>}\n",
      "anomaly_detection: labels = \n",
      "None\n",
      "anomaly_detection: mode = \n",
      "infer\n",
      "anomaly_detection: params = \n",
      "{'train_examples': 6400, 'k_principal_components_time': None, 'throttle_secs': 120, 'num_feat_anom_thresh': 300, 'min_time_anom_thresh': 1, 'train_batch_size': 64, 'feat_loss_weight': 1.0, 'enc_dnn_hidden_units': [64, 32, 16], 'previous_train_steps': 2400, 'output_dir': 'trained_model', 'do_eval': True, 'max_time_anom_thresh': 100, 'model_type': 'pca', 'learning_rate': 0.01, 'seq_len': 30, 'num_time_anom_thresh': 300, 'lstm_dropout_output_keep_probs': [1.0, 1.0, 1.0], 'feat_thresh_scl': 2.0, 'reverse_labels_sequence': True, 'train_file_pattern': 'data/labeled_val_mixed_seq.csv', 'reconstruction_epochs': 1.0, 'labeled_tune_thresh': True, 'feat_anom_thresh': None, 'latent_vector_size': 8, 'time_thresh_scl': 2.0, 'start_delay_secs': 60, 'min_feat_anom_thresh': 1, 'feat_names': ['tag_0', 'tag_1', 'tag_2', 'tag_3', 'tag_4'], 'eval_examples': 6400, 'max_feat_anom_thresh': 100, 'k_principal_components_feat': None, 'eval_file_pattern': 'data/labeled_val_mixed_seq.csv', 'enc_lstm_hidden_units': [64, 32, 16], 'eps': 1e-12, 'dnn_hidden_units': [1024, 256, 64], 'num_feat': 5, 'f_score_beta': 0.05, 'training_mode': 'tune_anomaly_thresholds', 'eval_batch_size': 64, 'dec_dnn_hidden_units': [16, 32, 64], 'dec_lstm_hidden_units': [16, 32, 64], 'autotune_principal_components': False, 'feat_defaults': [['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0'], ['0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0']], 'time_anom_thresh': None, 'time_loss_weight': 1.0}\n",
      "INFO:tensorflow:Done calling model_fn.\n",
      "INFO:tensorflow:Graph was finalized.\n",
      "INFO:tensorflow:Restoring parameters from trained_model/model.ckpt-2400\n",
      "INFO:tensorflow:Running local_init_op.\n",
      "INFO:tensorflow:Done running local_init_op.\n"
     ]
    }
   ],
   "source": [
    "pred_list = [pred for pred in estimator.predict(\n",
    "    input_fn = tf.estimator.inputs.numpy_input_fn(\n",
    "        x = dict_labeled_test_mixed_sequences_features,\n",
    "        y = None,\n",
    "        batch_size = 128,\n",
    "        num_epochs = 1,\n",
    "        shuffle = False,\n",
    "        queue_capacity = 1000))]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 175,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['0', '0', '1', '1', '0', '0', '0', '0', '1', '1'], dtype='<U468')"
      ]
     },
     "execution_count": 175,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr_test_labels[0:10]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Normal example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 176,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 176,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "norm_test_example_index = np.argmax(arr_test_labels=='0')\n",
    "norm_test_example_index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 177,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'X_feat_abs_recon_err': array([[4.06542392e-01, 8.79304075e-03, 2.04262998e-01, 1.82070867e-01,\n",
       "         2.27482358e-01],\n",
       "        [2.43523566e-01, 1.53880171e-01, 1.67462780e-01, 3.86093449e-01,\n",
       "         2.38915034e-01],\n",
       "        [4.33803489e-02, 4.97811558e-01, 4.05244983e-01, 6.56540502e-01,\n",
       "         9.88443506e-01],\n",
       "        [5.69053788e-01, 6.90088809e-02, 1.67999573e-01, 1.02574308e+00,\n",
       "         6.24964336e-01],\n",
       "        [2.16373003e-01, 5.25878642e-01, 1.05508914e-01, 2.21664147e+00,\n",
       "         1.00183852e+00],\n",
       "        [1.23700566e+00, 7.98738108e-01, 9.26727976e-01, 1.20863272e+00,\n",
       "         1.38086285e+00],\n",
       "        [5.55661857e-01, 7.53148617e-01, 9.01241022e-02, 2.05094423e+00,\n",
       "         1.03318993e-01],\n",
       "        [8.01495456e-01, 9.20209299e-01, 6.99483497e-01, 1.33970339e+00,\n",
       "         2.14283482e+00],\n",
       "        [9.58180368e-01, 7.96417170e-01, 1.78158597e-01, 1.19572021e+00,\n",
       "         6.42854948e-01],\n",
       "        [9.06838940e-01, 6.96916954e-01, 5.96443097e-01, 4.55878234e-01,\n",
       "         1.80296546e+00],\n",
       "        [1.24969584e+00, 2.53363938e-01, 4.46792477e-01, 2.39481290e-02,\n",
       "         2.02576250e+00],\n",
       "        [5.27271328e-01, 3.66575901e-01, 8.56592341e-01, 4.14945329e-01,\n",
       "         2.20678515e-01],\n",
       "        [9.31373337e-02, 5.87366331e-01, 1.32050938e+00, 3.03766677e-01,\n",
       "         2.58272749e+00],\n",
       "        [6.72411842e-01, 8.83488903e-03, 1.49076340e-01, 4.71600116e-01,\n",
       "         1.42707300e+00],\n",
       "        [5.50114399e-01, 6.67074171e-02, 1.07226134e+00, 3.61748616e-01,\n",
       "         1.45187369e+00],\n",
       "        [8.55298806e-01, 9.06837613e-01, 9.30334621e-01, 4.88347127e-01,\n",
       "         1.09976527e+00],\n",
       "        [1.49673841e-01, 2.51567597e-01, 8.57669572e-01, 2.49587413e-01,\n",
       "         3.84194941e-01],\n",
       "        [3.57771371e-01, 4.43675024e-01, 1.10661925e+00, 1.24875036e+00,\n",
       "         3.08757292e-01],\n",
       "        [3.60212817e-01, 8.68285441e-01, 8.56616765e-02, 8.16853770e-01,\n",
       "         1.04909431e+00],\n",
       "        [1.77439007e-01, 1.17293561e-01, 8.20114018e-01, 8.36938577e-01,\n",
       "         1.74091516e-01],\n",
       "        [7.89162948e-01, 2.95518708e-01, 8.14436884e-01, 8.56982128e-01,\n",
       "         1.56926713e+00],\n",
       "        [5.59709125e-01, 7.84231593e-01, 1.43410928e+00, 8.26617979e-03,\n",
       "         1.02379227e+00],\n",
       "        [5.18013621e-01, 1.33970835e-01, 2.72680537e-01, 1.08303017e+00,\n",
       "         6.73040350e-01],\n",
       "        [8.68981161e-01, 1.04572411e+00, 1.25383685e+00, 2.74658314e-03,\n",
       "         1.21155633e+00],\n",
       "        [5.64001741e-01, 5.72396972e-01, 1.42006914e-02, 3.41799271e-01,\n",
       "         9.27309972e-01],\n",
       "        [9.14243889e-01, 1.29617172e+00, 1.27647832e+00, 1.31028566e+00,\n",
       "         8.79743667e-01],\n",
       "        [4.50051618e-01, 1.35971413e+00, 2.08396378e-01, 1.44969081e-02,\n",
       "         2.11271128e+00],\n",
       "        [2.78158265e-01, 9.91581806e-01, 1.39525701e+00, 1.32449663e+00,\n",
       "         3.59491003e-04],\n",
       "        [1.08645488e+00, 1.50595513e+00, 1.15267371e+00, 6.44812970e-01,\n",
       "         1.42345191e+00],\n",
       "        [2.68843141e-01, 8.25875845e-01, 6.39611365e-01, 5.69169745e-01,\n",
       "         6.63125989e-01]]),\n",
       " 'X_time_abs_recon_err': array([[0.2387262 , 0.23806245, 0.06426973, 0.17451493, 0.13138722],\n",
       "        [1.71210414, 0.94379529, 1.17323512, 1.19345535, 1.71685131],\n",
       "        [0.77009217, 0.40356869, 0.53615116, 0.23810677, 1.27030338],\n",
       "        [1.8550592 , 1.15324817, 1.00002392, 0.11046809, 0.62467266],\n",
       "        [0.9246657 , 0.5430099 , 0.47339558, 1.54984439, 1.69929419],\n",
       "        [1.96084937, 1.11306952, 1.35405476, 0.70157206, 0.93622426],\n",
       "        [1.41859424, 1.0468737 , 0.34593706, 1.33176741, 0.65544656],\n",
       "        [1.15687249, 0.71911646, 0.69971857, 1.37252833, 2.01468246],\n",
       "        [1.67406014, 1.15390061, 0.65242947, 0.59033063, 0.00407262],\n",
       "        [0.83936839, 0.54990177, 0.47510973, 0.68688098, 2.02285829],\n",
       "        [1.47018764, 0.89755296, 0.93669165, 0.52015477, 1.65648941],\n",
       "        [0.82589198, 0.29112126, 0.94031151, 0.52182531, 0.3480788 ],\n",
       "        [1.1711468 , 0.39137779, 1.53027948, 0.76637874, 2.04119199],\n",
       "        [0.05918243, 0.01828179, 0.07791427, 0.28348054, 1.66979161],\n",
       "        [1.20052107, 0.37943891, 1.57100877, 0.87501796, 0.98779564],\n",
       "        [0.31553308, 0.19966155, 0.22472648, 0.84353416, 1.71658727],\n",
       "        [1.03884026, 0.22542365, 1.497292  , 0.27859945, 1.10276429],\n",
       "        [0.04520044, 0.20417101, 0.55365729, 1.54733864, 0.65317302],\n",
       "        [0.16974833, 0.19983549, 0.71877595, 0.38981943, 1.51806418],\n",
       "        [0.02148526, 0.28873639, 0.6036405 , 0.85786459, 0.03846198],\n",
       "        [0.08164099, 0.42553084, 1.03591383, 0.69530759, 1.9670486 ],\n",
       "        [0.14047743, 0.81804582, 1.54806734, 0.23976997, 1.31587523],\n",
       "        [0.39601301, 0.37537525, 0.13190588, 1.22746062, 0.58978104],\n",
       "        [0.37982364, 0.97533415, 1.43194312, 0.33274011, 1.43575808],\n",
       "        [0.48138871, 0.45088178, 0.05137286, 0.66459605, 1.01885781],\n",
       "        [0.66112987, 1.20489455, 1.44094289, 1.4202866 , 1.11190269],\n",
       "        [0.7241221 , 0.87003195, 0.46482377, 0.18825905, 2.31506357],\n",
       "        [0.64672413, 1.0706313 , 1.21706751, 1.05973427, 0.31695937],\n",
       "        [1.49555633, 1.69402093, 0.86294756, 0.80327728, 1.08756119],\n",
       "        [1.06319709, 1.02088505, 0.28910438, 0.03688981, 1.12314481]]),\n",
       " 'feat_anom_flags': 0,\n",
       " 'feat_anom_thresh_var': 7.622073578595318,\n",
       " 'mahalanobis_dist_feat': array([5.63307126, 5.35056584, 5.87925282, 6.3331951 , 6.13815076]),\n",
       " 'mahalanobis_dist_time': array([2.64645199, 2.12615018, 1.50531246, 2.53155325, 2.53027203,\n",
       "        2.06132674, 2.7111372 , 2.30910901, 2.70335386, 1.7097514 ,\n",
       "        1.46038017, 1.83018915, 2.59480484, 2.33296924, 2.3563784 ,\n",
       "        1.79183225, 2.64971575, 2.70926562, 1.53947629, 2.3646671 ,\n",
       "        2.17842128, 3.20601166, 2.26995217, 2.7189987 , 1.73845416,\n",
       "        3.18385807, 2.54791886, 2.68499148, 2.45454269, 2.3910789 ]),\n",
       " 'time_anom_flags': 0,\n",
       " 'time_anom_thresh_var': 4.311036789297659}"
      ]
     },
     "execution_count": 177,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pred_list[norm_test_example_index]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 179,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.5/dist-packages/seaborn/timeseries.py:183: UserWarning: The `tsplot` function is deprecated and will be removed in a future release. Please update your code to use the new `lineplot` function.\n",
      "  warnings.warn(msg, UserWarning)\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "flatui = [\"#9b59b6\", \"#3498db\", \"#95a5a6\", \"#e74c3c\", \"#34495e\", \"#2ecc71\"]\n",
    "for i, arr in enumerate(np.split(\n",
    "    ary=pred_list[norm_test_example_index][\"X_time_abs_recon_err\"].flatten(),\n",
    "    indices_or_sections=len(arguments[\"feat_names\"]),\n",
    "    axis=0)):\n",
    "  sns.tsplot(arr, color = flatui[i%len(flatui)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 181,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "flatui = [\"#9b59b6\", \"#3498db\", \"#95a5a6\", \"#e74c3c\", \"#34495e\", \"#2ecc71\"]\n",
    "for i, arr in enumerate(np.split(\n",
    "    ary=pred_list[norm_test_example_index][\"X_feat_abs_recon_err\"].flatten(),\n",
    "    indices_or_sections=len(arguments[\"feat_names\"]),\n",
    "    axis=0)):\n",
    "  sns.tsplot(arr, color = flatui[i%len(flatui)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Anomalous Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 182,
   "metadata": {},
   "outputs": [],
   "source": [
    "anom_test_example_index = np.argmax(arr_test_labels=='1')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 183,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'X_feat_abs_recon_err': array([[2.20091636e-01, 4.89022475e-01, 4.80802955e-01, 5.28501440e-02,\n",
       "         6.59545666e-02],\n",
       "        [6.11578846e-01, 2.17320564e-01, 3.42855452e-01, 1.13763413e-01,\n",
       "         5.10599636e-01],\n",
       "        [1.40502485e+00, 3.20028084e-01, 1.10879740e-01, 1.51722790e+00,\n",
       "         2.35790609e+00],\n",
       "        [1.09457012e+00, 6.53823461e-02, 5.27684428e-01, 5.51594081e-02,\n",
       "         8.39880825e-01],\n",
       "        [2.07326668e+00, 3.04992047e-01, 1.23865855e+00, 3.25651192e+00,\n",
       "         2.56814724e+00],\n",
       "        [2.95969479e+00, 7.98420887e-01, 1.57168229e+00, 1.19865531e-01,\n",
       "         3.32933252e+00],\n",
       "        [2.72137530e+00, 9.87531010e-01, 1.01988877e+00, 3.39803337e+00,\n",
       "         2.65179295e+00],\n",
       "        [4.72654250e+00, 8.58142045e-01, 1.96423862e+00, 2.60554483e-01,\n",
       "         4.01787101e+00],\n",
       "        [1.71581273e+00, 2.03533444e-01, 4.19354836e-01, 1.74875933e+00,\n",
       "         6.72350321e-01],\n",
       "        [5.41606747e+00, 4.25023768e-01, 2.50377172e+00, 1.24692571e+00,\n",
       "         5.40437022e+00],\n",
       "        [1.15598618e+00, 9.45567368e-01, 4.47519523e-01, 1.04896452e+00,\n",
       "         4.40753026e-01],\n",
       "        [7.04459207e+00, 6.26201130e-01, 3.01627802e+00, 2.83935769e+00,\n",
       "         5.13761781e+00],\n",
       "        [4.66852456e-01, 4.65192300e-01, 1.01015367e+00, 6.61306414e-01,\n",
       "         1.81189952e+00],\n",
       "        [7.25033884e+00, 6.47880248e-01, 2.43534389e+00, 3.36865744e+00,\n",
       "         4.58608219e+00],\n",
       "        [2.35254693e+00, 2.67510921e-01, 1.65174070e+00, 1.09062692e+00,\n",
       "         2.91648847e+00],\n",
       "        [6.48440510e+00, 9.11884355e-01, 2.16440882e+00, 3.91307070e+00,\n",
       "         4.24408391e+00],\n",
       "        [4.28642980e+00, 5.77305821e-01, 2.86972068e+00, 1.81625579e+00,\n",
       "         3.90179587e+00],\n",
       "        [5.71555699e+00, 3.14876780e-02, 1.12963630e+00, 3.92829674e+00,\n",
       "         4.02699370e+00],\n",
       "        [5.67607705e+00, 5.41046810e-01, 2.81490829e+00, 2.13402659e+00,\n",
       "         3.78560120e+00],\n",
       "        [4.61799910e+00, 8.41078476e-01, 7.36301037e-01, 2.99046651e+00,\n",
       "         3.71014560e+00],\n",
       "        [6.88093992e+00, 5.08294954e-01, 2.93924893e+00, 2.75301722e+00,\n",
       "         4.02323826e+00],\n",
       "        [1.21543939e+01, 2.26148013e+01, 9.74466726e+00, 2.34255208e+00,\n",
       "         1.18936530e+01],\n",
       "        [3.64836001e+01, 1.28594625e+01, 3.95383596e-01, 5.15378070e-01,\n",
       "         1.52979331e+01],\n",
       "        [1.12615964e+01, 5.16661416e+00, 2.96410588e+01, 1.64366922e+01,\n",
       "         3.75294687e+01],\n",
       "        [1.46297769e+01, 4.22132257e+01, 2.42648770e+01, 8.00584323e+00,\n",
       "         2.01875788e+01],\n",
       "        [2.05392104e+01, 2.51096364e+01, 1.18556623e+01, 7.42107438e+00,\n",
       "         1.19218047e+00],\n",
       "        [2.06686875e+01, 3.62527751e+00, 1.85993676e+01, 1.44083073e+01,\n",
       "         1.59999423e+01],\n",
       "        [6.72559924e-01, 2.53459984e+01, 1.84942137e+01, 3.63062968e+01,\n",
       "         1.08808125e+01],\n",
       "        [1.32977392e+01, 5.22512502e-01, 2.83577511e+01, 2.64691662e+00,\n",
       "         2.02397610e+01],\n",
       "        [7.79350784e+00, 2.40482454e+01, 1.26740482e+01, 1.65084524e+00,\n",
       "         2.99439729e+01]]),\n",
       " 'X_time_abs_recon_err': array([[2.08028006e-01, 4.06074088e-02, 3.16827837e-01, 5.53325305e-02,\n",
       "         1.93626067e-02],\n",
       "        [2.21290331e+00, 1.24952442e+00, 1.48379048e+00, 1.27114680e+00,\n",
       "         1.29309630e+00],\n",
       "        [5.56258014e-01, 2.13570217e-01, 5.25117209e-01, 5.16760434e-01,\n",
       "         1.66495448e+00],\n",
       "        [1.68746271e+00, 9.10985072e-01, 1.21706928e+00, 7.66361798e-01,\n",
       "         1.12076435e+00],\n",
       "        [9.81167006e-01, 7.02310218e-01, 2.29181751e-01, 1.52793058e+00,\n",
       "         1.48860795e+00],\n",
       "        [1.82488090e+00, 1.07750447e+00, 1.19596983e+00, 2.70795754e-01,\n",
       "         1.38133367e+00],\n",
       "        [1.28389011e+00, 8.93250277e-01, 4.18165469e-01, 1.34024838e+00,\n",
       "         9.60789597e-01],\n",
       "        [1.46950424e+00, 9.70123314e-01, 7.06922659e-01, 1.36434502e+00,\n",
       "         1.22233619e+00],\n",
       "        [1.49638822e+00, 7.61264115e-01, 1.25496012e+00, 1.23531526e-01,\n",
       "         8.09975994e-01],\n",
       "        [3.65464636e-01, 2.20702545e-01, 2.59014418e-01, 1.37601918e+00,\n",
       "         1.61850314e+00],\n",
       "        [1.47216149e+00, 7.46559042e-01, 1.27136616e+00, 2.54419288e-01,\n",
       "         1.42608058e+00],\n",
       "        [2.28129658e-01, 8.35569140e-02, 6.24262070e-01, 4.02718069e-01,\n",
       "         2.42535487e-01],\n",
       "        [1.23162657e+00, 5.31286101e-01, 1.29756103e+00, 1.07988637e+00,\n",
       "         1.19576066e+00],\n",
       "        [8.02310169e-02, 1.82516991e-02, 1.91650564e-01, 2.02718631e-02,\n",
       "         1.64945966e+00],\n",
       "        [7.02055374e-01, 1.22638739e-01, 1.14655436e+00, 4.87627238e-01,\n",
       "         5.83102611e-01],\n",
       "        [3.81475922e-01, 2.54728245e-01, 2.57768133e-01, 7.27554426e-01,\n",
       "         2.30243024e+00],\n",
       "        [6.28214545e-01, 1.50466656e-01, 1.57330493e+00, 1.62521714e-01,\n",
       "         4.70820093e-01],\n",
       "        [6.15159803e-02, 4.01247728e-01, 1.07765623e+00, 1.22054797e+00,\n",
       "         1.35142652e+00],\n",
       "        [1.80031135e-01, 1.81909206e-01, 6.89491072e-01, 4.27519177e-01,\n",
       "         1.68753194e+00],\n",
       "        [5.53923518e-01, 8.88749971e-01, 9.23178947e-01, 9.35206891e-01,\n",
       "         2.08691045e-02],\n",
       "        [1.61634310e-01, 9.23262998e-02, 4.29606875e-01, 4.75194928e-01,\n",
       "         2.05360779e+00],\n",
       "        [2.31847844e+00, 8.72940582e+00, 1.47770736e+01, 3.72855611e+00,\n",
       "         1.35528444e+01],\n",
       "        [3.27428222e+01, 2.52338206e+01, 7.87765080e+00, 2.84131992e+00,\n",
       "         2.05530883e+01],\n",
       "        [2.64926935e+00, 1.44025643e+01, 2.58115507e+01, 1.59691672e+01,\n",
       "         3.74928063e+01],\n",
       "        [9.47087887e-01, 1.67387929e+01, 3.79462066e+01, 1.10299243e+01,\n",
       "         2.45368809e+01],\n",
       "        [2.68270269e+01, 1.89070616e+01, 9.56260587e+00, 6.86713729e+00,\n",
       "         1.71969733e-01],\n",
       "        [1.11324957e+01, 1.34459599e+01, 9.02934044e+00, 1.68435085e+01,\n",
       "         1.25178653e+01],\n",
       "        [9.65120947e+00, 1.76084247e+01, 2.09368689e+01, 3.75362365e+01,\n",
       "         9.55532079e+00],\n",
       "        [8.45694847e-01, 1.11541176e+01, 2.51324505e+01, 4.42335281e+00,\n",
       "         2.38504226e+01],\n",
       "        [1.00306500e+01, 6.60267231e+00, 6.10845145e+00, 8.23902811e-03,\n",
       "         3.12778018e+01]]),\n",
       " 'feat_anom_flags': 1,\n",
       " 'feat_anom_thresh_var': 7.622073578595318,\n",
       " 'mahalanobis_dist_feat': array([202.64024689, 225.56980347, 154.63026506, 136.48202862,\n",
       "        185.13782136]),\n",
       " 'mahalanobis_dist_time': array([  2.7989144 ,   2.75851988,   1.49925776,   1.61354544,\n",
       "          2.71566664,   2.08480493,   2.3004298 ,   2.13372252,\n",
       "          2.22933365,   2.34350825,   1.95899252,   2.04458542,\n",
       "          1.77994896,   2.56832118,   2.07960958,   2.37767421,\n",
       "          3.09837394,   2.33314551,   1.62869057,   2.51174473,\n",
       "          2.12338228,  43.34797462,  65.07607862,  96.50167465,\n",
       "        109.55837977,  47.74114463,  52.75248209, 109.70178384,\n",
       "         74.74392639,  51.42757755]),\n",
       " 'time_anom_flags': 1,\n",
       " 'time_anom_thresh_var': 4.311036789297659}"
      ]
     },
     "execution_count": 183,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pred_list[anom_test_example_index]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 184,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "flatui = [\"#9b59b6\", \"#3498db\", \"#95a5a6\", \"#e74c3c\", \"#34495e\", \"#2ecc71\"]\n",
    "for i, arr in enumerate(np.split(\n",
    "    ary=pred_list[anom_test_example_index][\"X_time_abs_recon_err\"].flatten(),\n",
    "    indices_or_sections=len(arguments[\"feat_names\"]),\n",
    "    axis=0)):\n",
    "  sns.tsplot(arr, color = flatui[i%len(flatui)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 185,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "flatui = [\"#9b59b6\", \"#3498db\", \"#95a5a6\", \"#e74c3c\", \"#34495e\", \"#2ecc71\"]\n",
    "for i, arr in enumerate(np.split(\n",
    "    ary=pred_list[anom_test_example_index][\"X_feat_abs_recon_err\"].flatten(),\n",
    "    indices_or_sections=len(arguments[\"feat_names\"]),\n",
    "    axis=0)):\n",
    "  sns.tsplot(arr, color = flatui[i%len(flatui)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
